This is an automated email from the ASF dual-hosted git repository.
bugraoz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 180a9d837a7 feat (airflowctl): Initial airflowctl docs (#49024)
180a9d837a7 is described below
commit 180a9d837a70a181d7766d52995b9d8ca668640e
Author: Bugra Ozturk <[email protected]>
AuthorDate: Fri May 16 00:20:35 2025 +0200
feat (airflowctl): Initial airflowctl docs (#49024)
* feat (airflowctl): AIP-81 Initial airflowctl docs and update docs
according to comments
---
airflow-ctl/README.md | 22 ++
airflow-ctl/docs/changelog.rst | 31 +++
airflow-ctl/docs/cli-and-env-variables-ref.rst | 55 ++++
airflow-ctl/docs/conf.py | 294 +++++++++++++++++++++
airflow-ctl/docs/howto/index.rst | 77 ++++++
airflow-ctl/docs/index.rst | 36 +++
airflow-ctl/docs/installation/dependencies.rst | 26 ++
airflow-ctl/docs/installation/index.rst | 134 ++++++++++
.../docs/installation/installing-from-pypi.rst | 163 ++++++++++++
.../docs/installation/installing-from-sources.rst | 163 ++++++++++++
airflow-ctl/docs/installation/prerequisites.rst | 63 +++++
.../docs/installation/supported-versions.rst | 42 +++
.../airflowctl/__init__.py => docs/redirects.txt} | 13 +-
airflow-ctl/docs/security.rst | 27 ++
airflow-ctl/docs/start.rst | 42 +++
airflow-ctl/docs/static/exampleinclude.css | 86 ++++++
airflow-ctl/docs/static/gh-jira-links.js | 36 +++
airflow-ctl/docs/static/redirects.js | 29 ++
airflow-ctl/docs/templates/footer.html | 29 ++
airflow-ctl/pyproject.toml | 22 +-
airflow-ctl/src/airflowctl/__init__.py | 2 +-
airflow-ctl/src/airflowctl/api/operations.py | 5 +-
airflow-ctl/src/airflowctl/ctl/cli_config.py | 37 ++-
airflow-ctl/src/airflowctl/ctl/cli_parser.py | 12 +-
airflow-ctl/src/airflowctl/exceptions.py | 4 +
.../tests/airflow_ctl/api/test_operations.py | 12 +-
.../tests/airflow_ctl/ctl/test_cli_config.py | 14 +-
devel-common/src/docs/utils/conf_constants.py | 5 +
.../src/sphinx_exts/docs_build/docs_builder.py | 5 +
29 files changed, 1455 insertions(+), 31 deletions(-)
diff --git a/airflow-ctl/README.md b/airflow-ctl/README.md
index 6c2908b7431..8e543717d9b 100644
--- a/airflow-ctl/README.md
+++ b/airflow-ctl/README.md
@@ -33,11 +33,14 @@ A command-line tool for interacting with Apache Airflow
instances through the Ai
- Python 3.9 or later (compatible with Python >= 3.9 and < 3.13)
- Network access to an Apache Airflow instance with REST API enabled
+- Keyring backend installed in operating system for secure token storage
## Usage
Access the tool from your terminal:
+### Command Line
+
```bash
airflowctl --help
```
@@ -45,3 +48,22 @@ airflowctl --help
## Contributing
Want to help improve Apache Airflow? Check out our [contributing
documentation](https://github.com/apache/airflow/blob/main/contributing-docs/README.rst).
+
+### Additional Contribution Guidelines
+
+- Please ensure API is running while doing development testing.
+- There are two ways to have a CLI command,
+ - Auto Generated Commands
+ - Implemented Commands
+
+#### Auto Generated Commands
+
+Auto generation of commands directly from operations methods under
`airflow-ctl/src/airflowctl/api/operations.py`.
+Whenever operation is mapped with proper datamodel and response model, it will
be automatically added to the command.
+
+You can check each command with `airflowctl <command> --help` to see the
available options.
+
+#### Implemented Commands
+
+Implemented commands are the ones which are not auto generated and need to be
implemented manually.
+You can check the implemented commands under
`airflow-ctl/src/airflowctl/clt/commands/`.
diff --git a/airflow-ctl/docs/changelog.rst b/airflow-ctl/docs/changelog.rst
new file mode 100644
index 00000000000..50a8a3b7757
--- /dev/null
+++ b/airflow-ctl/docs/changelog.rst
@@ -0,0 +1,31 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+
+.. NOTE TO CONTRIBUTORS:
+ Please, only add notes to the Changelog just below the "Changelog" header
when there are some breaking changes
+ and you want to add an explanation to the users on how they are supposed to
deal with them.
+ The changelog is updated and maintained semi-automatically by release
manager.
+
+``apache-airflow-ctl``
+
+Changelog
+---------
+
+1.0.0
+.....
+Initial version of the Airflow CTL.
diff --git a/airflow-ctl/docs/cli-and-env-variables-ref.rst
b/airflow-ctl/docs/cli-and-env-variables-ref.rst
new file mode 100644
index 00000000000..db45a5d2e27
--- /dev/null
+++ b/airflow-ctl/docs/cli-and-env-variables-ref.rst
@@ -0,0 +1,55 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Command Line Interface and Environment Variables Reference
+==========================================================
+
+Command Line Interface
+''''''''''''''''''''''
+
+Airflow CTL has a very rich command line interface that allows for
+many types of operation on a DAG, starting services, and supporting
+development and testing.
+
+.. note::
+ For more information on usage CLI, see :doc:`cli-and-env-variables-ref`
+
+.. contents:: Content
+ :local:
+ :depth: 2
+
+.. argparse::
+ :module: airflowctl.ctl.cli_parser
+ :func: get_parser
+ :prog: airflowctl
+
+Environment Variables
+'''''''''''''''''''''
+
+.. envvar:: AIRFLOW_CLI_TOKEN
+
+ The token used to authenticate with the Airflow API. This is only
+ required if you are using the Airflow API and have not set up
+ authentication using a different method. If username and password hasn't
been used.
+
+.. envvar:: AIRFLOW_CLI_ENVIRONMENT
+
+ Environment name to use for the CLI. This is used to determine
+ which environment to use when running the CLI. This is only
+ required if you have multiple environments set up and want to
+ specify which one to use. If not set, the default environment
+ will be used which is production.
diff --git a/airflow-ctl/docs/conf.py b/airflow-ctl/docs/conf.py
new file mode 100644
index 00000000000..d075866c8bd
--- /dev/null
+++ b/airflow-ctl/docs/conf.py
@@ -0,0 +1,294 @@
+# Disable Flake8 because of all the sphinx imports
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Configuration of Providers docs building."""
+
+from __future__ import annotations
+
+import logging
+import os
+import pathlib
+import re
+from typing import Any
+
+from docs.utils.conf_constants import (
+ AIRFLOW_CTL_DOC_STATIC_PATH,
+ AIRFLOW_CTL_SRC_PATH,
+ AIRFLOW_FAVICON_PATH,
+ AUTOAPI_OPTIONS,
+ BASIC_AUTOAPI_IGNORE_PATTERNS,
+ BASIC_SPHINX_EXTENSIONS,
+ REDOC_SCRIPT_URL,
+ SMARTQUOTES_EXCLUDES,
+ SPELLING_WORDLIST_PATH,
+ SPHINX_DESIGN_STATIC_PATH,
+ SPHINX_REDOC_EXTENSIONS,
+ SUPPRESS_WARNINGS,
+ filter_autoapi_ignore_entries,
+ get_autodoc_mock_imports,
+ get_configs_and_deprecations,
+ get_google_intersphinx_mapping,
+ get_html_context,
+ get_html_sidebars,
+ get_html_theme_options,
+ get_intersphinx_mapping,
+ get_rst_epilogue,
+ get_rst_filepath_from_path,
+ skip_util_classes_extension,
+)
+from packaging.version import Version, parse as parse_version
+
+import airflowctl
+from airflow.configuration import retrieve_configuration_description
+
+PACKAGE_NAME = "apache-airflow-ctl"
+PACKAGE_VERSION = airflowctl.__version__
+SYSTEM_TESTS_DIR: pathlib.Path | None
+# SYSTEM_TESTS_DIR = AIRFLOW_REPO_ROOT_PATH / "airflow-ctl" / "tests" /
"system" / "core"
+
+os.environ["AIRFLOW_PACKAGE_NAME"] = PACKAGE_NAME
+
+# Hack to allow changing for piece of the code to behave differently while
+# the docs are being built. The main objective was to alter the
+# behavior of the utils.apply_default that was hiding function headers
+os.environ["BUILDING_AIRFLOW_DOCS"] = "TRUE"
+
+# General information about the project.
+project = PACKAGE_NAME
+# # The version info for the project you're documenting
+version = PACKAGE_VERSION
+# The full version, including alpha/beta/rc tags.
+release = PACKAGE_VERSION
+
+rst_epilog = get_rst_epilogue(PACKAGE_VERSION, False)
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+smartquotes_excludes = SMARTQUOTES_EXCLUDES
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = BASIC_SPHINX_EXTENSIONS
+
+# -- Options for sphinxcontrib.redoc
-------------------------------------------
+# See: https://sphinxcontrib-redoc.readthedocs.io/en/stable/
+
+extensions.extend(SPHINX_REDOC_EXTENSIONS)
+redoc_script_url = REDOC_SCRIPT_URL
+
+extensions.extend(
+ [
+ "autoapi.extension",
+ "sphinx_jinja",
+ "sphinx.ext.graphviz",
+ "sphinxcontrib.httpdomain",
+ "extra_files_with_substitutions",
+ ]
+)
+
+exclude_patterns = [
+ # We only link to selected subpackages.
+ "_api/airflowctl/index.rst",
+ "_api/airflowctl/api/*",
+ "_api/airflowctl/api/datamodels/*",
+ "_api/airflowctl/ctl/*",
+ "_api/airflowctl/exceptions/index.rst",
+ "_api/airflowctl/utils/*",
+ "README.rst",
+]
+
+# Exclude top-level packages
+# do not exclude these top-level modules from the doc build:
+ALLOWED_TOP_LEVEL_FILES = ("exceptions.py",)
+
+
+def add_airflow_ctl_exclude_patterns_to_sphinx(exclude_patterns: list[str]):
+ """
+ Add excluded files to Sphinx exclude patterns.
+
+ Excludes all files from autoapi except the ones we want to allow.
+
+ :param root: The root directory of the package.
+ :param allowed_top_level_files: Tuple of allowed top-level files.
+ :param browsable_packages: Set of browsable packages.
+ :param browsable_utils: Set of browsable utils.
+ :param models_included: Set of included models.
+ """
+ # first - excluded everything that is not allowed or browsable
+ root = AIRFLOW_CTL_SRC_PATH / "airflowctl"
+ for path in root.iterdir():
+ if path.is_file() and path.name not in ALLOWED_TOP_LEVEL_FILES:
+ exclude_patterns.append(get_rst_filepath_from_path(path,
root.parent))
+ print(f"Excluding {path} from Sphinx docs")
+
+
+add_airflow_ctl_exclude_patterns_to_sphinx(exclude_patterns)
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["templates"]
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+keep_warnings = True
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = "sphinx_airflow_theme"
+
+html_title = "Airflow CTL Documentation"
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+html_short_title = ""
+
+# given, this must be the name of an image file that is the favicon of the
docs
+html_favicon = AIRFLOW_FAVICON_PATH.as_posix()
+
+# Custom static files (such as style sheets) here,
+html_static_path = [AIRFLOW_CTL_DOC_STATIC_PATH.as_posix(),
SPHINX_DESIGN_STATIC_PATH.as_posix()]
+
+# A list of JavaScript filenames.
+html_js_files = ["gh-jira-links.js", "redirects.js"]
+
+# Substitute in links
+manual_substitutions_in_generated_html = [
+ "installation/installing-from-pypi.html",
+ "installation/installing-from-sources.html",
+ "installation/prerequisites.html",
+]
+
+html_css_files = ["custom.css"]
+
+html_sidebars = get_html_sidebars(PACKAGE_VERSION)
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = False
+
+# html theme options
+html_theme_options: dict[str, Any] = get_html_theme_options()
+
+conf_py_path = "/airflow-ctl/docs/"
+# A dictionary of values to pass into the template engine's context for all
pages.
+html_context = get_html_context(conf_py_path)
+
+# -- Options for sphinx_jinja ------------------------------------------
+# See: https://github.com/tardyp/sphinx-jinja
+airflowctl_version: Version = parse_version(
+ re.search( # type: ignore[union-attr,arg-type]
+ r"__version__ = \"([0-9.]*)(\.dev[0-9]*)?\"",
+ (AIRFLOW_CTL_SRC_PATH / "airflowctl" / "__init__.py").read_text(),
+ ).groups(0)[0]
+)
+
+
+config_descriptions =
retrieve_configuration_description(include_providers=False)
+configs, deprecated_options = get_configs_and_deprecations(airflowctl_version,
config_descriptions)
+
+jinja_contexts = {
+ "config_ctx": {"configs": configs, "deprecated_options":
deprecated_options},
+ "quick_start_ctx": {"doc_root_url":
f"https://airflow.apache.org/docs/apache-airflow/{PACKAGE_VERSION}/"},
+ "official_download_page": {
+ "base_url": f"https://downloads.apache.org/airflow/{PACKAGE_VERSION}",
+ "closer_lua_url":
f"https://www.apache.org/dyn/closer.lua/airflow/{PACKAGE_VERSION}",
+ "airflow_version": PACKAGE_VERSION,
+ },
+}
+
+# -- Options for sphinx.ext.autodoc
--------------------------------------------
+# See: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
+
+# This value contains a list of modules to be mocked up. This is useful when
some external dependencies
+# are not met at build time and break the building process.
+autodoc_mock_imports = get_autodoc_mock_imports()
+# The default options for autodoc directives. They are applied to all autodoc
directives automatically.
+autodoc_default_options = {"show-inheritance": True, "members": True}
+
+autodoc_typehints = "description"
+autodoc_typehints_description_target = "documented"
+autodoc_typehints_format = "short"
+
+
+# -- Options for sphinx.ext.intersphinx
----------------------------------------
+# See: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
+
+# This config value contains names of other projects that should
+# be linked to in this documentation.
+# Inventories are only downloaded once by exts/docs_build/fetch_inventories.py.
+intersphinx_mapping = get_intersphinx_mapping()
+intersphinx_mapping.update(get_google_intersphinx_mapping())
+
+# -- Options for sphinx.ext.viewcode
-------------------------------------------
+# See: https://www.sphinx-doc.org/es/master/usage/extensions/viewcode.html
+
+# If this is True, viewcode extension will emit viewcode-follow-imported event
to resolve the name of
+# the module by other extensions. The default is True.
+viewcode_follow_imported_members = True
+
+# -- Options for sphinx-autoapi
------------------------------------------------
+# See: https://sphinx-autoapi.readthedocs.io/en/latest/config.html
+
+# your API documentation from.
+autoapi_dirs = [AIRFLOW_CTL_SRC_PATH.as_posix()]
+
+# A directory that has user-defined templates to override our default
templates.
+autoapi_template_dir = "autoapi_templates"
+
+# A list of patterns to ignore when finding files
+autoapi_ignore = BASIC_AUTOAPI_IGNORE_PATTERNS
+
+# filter logging
+autoapi_log = logging.getLogger("sphinx.autoapi.mappers.base")
+autoapi_log.addFilter(filter_autoapi_ignore_entries)
+
+# Keep the AutoAPI generated files on the filesystem after the run.
+# Useful for debugging.
+autoapi_keep_files = True
+
+# Relative path to output the AutoAPI files into. This can also be used to
place the generated documentation
+# anywhere in your documentation hierarchy.
+autoapi_root = "_api"
+
+# Whether to insert the generated documentation into the TOC tree. If this is
False, the default AutoAPI
+# index page is not generated and you will need to include the generated
documentation in a
+# TOC tree entry yourself.
+autoapi_add_toctree_entry = False
+
+# By default autoapi will include private members -- we don't want that!
+autoapi_options = AUTOAPI_OPTIONS
+
+suppress_warnings = SUPPRESS_WARNINGS
+
+# -- Options for ext.exampleinclude
--------------------------------------------
+exampleinclude_sourceroot = os.path.abspath("..")
+
+# -- Options for ext.redirects
-------------------------------------------------
+redirects_file = "redirects.txt"
+
+# -- Options for sphinxcontrib-spelling
----------------------------------------
+spelling_word_list_filename = [SPELLING_WORDLIST_PATH.as_posix()]
+spelling_exclude_patterns = ["project.rst", "changelog.rst"]
+
+spelling_ignore_contributor_names = False
+spelling_ignore_importable_modules = True
+
+graphviz_output_format = "svg"
+
+
+def setup(sphinx):
+ sphinx.connect("autoapi-skip-member", skip_util_classes_extension)
diff --git a/airflow-ctl/docs/howto/index.rst b/airflow-ctl/docs/howto/index.rst
new file mode 100644
index 00000000000..78de222b450
--- /dev/null
+++ b/airflow-ctl/docs/howto/index.rst
@@ -0,0 +1,77 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+
+
+How-to Guides
+=============
+
+Setting up the sandbox in the :doc:`/start` section was easy;
+building a production-grade environment requires a bit more work!
+
+These how-to guides will step you through common tasks in using and
+configuring an Airflow CTL environment.
+
+
+How to use Airflow CTL
+----------------------
+
+**Important Note**
+''''''''''''''''''
+Airflow CTL needs the Airflow API running to be able to work. Please, see the
login section below before use.
+Otherwise, you may get errors.
+
+Login
+'''''
+Airflow CTL needs to be able to connect to the Airflow API. You should pass
API URL as a parameter to the command
+``--api-url``. The URL should be in the form of ``http(s)://<host>:<port>``.
+You can also set the environment variable ``AIRFLOW_CLI_TOKEN`` to the token
to use for authentication.
+
+There are two ways to authenticate with the Airflow API:
+1. Using a token acquired from the Airflow API
+
+.. code-block:: bash
+
+ airflowctl auth login --api-url <api_url> --api-token <token> --env
<env_name:production>
+
+2. Using a username and password
+
+
+.. code-block:: bash
+
+ airflowctl auth login --api-url <api_url> --username <username> --password
<password> --env <env_name:production>
+
+3. (optional) Using a token acquired from the Airflow API and username and
password
+
+.. code-block:: bash
+
+ export AIRFLOW_CLI_TOKEN=<token>
+ airflowctl auth login --api-url <api_url> --env <env_name>
+
+In both cases token is securely stored in the keyring backend. Only
configuration persisted in ``~/.config/airflow`` file
+is the API URL and the environment name. The token is stored in the keyring
backend and is not persisted in the
+configuration file. The keyring backend is used to securely store the token
and is not accessible to the user.
+
+
+For more information use
+
+.. code-block:: bash
+
+ airflowctl auth login --help
+
+You are ready to use Airflow CTL now. You can use the command ``airflowctl
--help`` to see the list of available commands.
+Please, also see :doc:`/cli-and-env-variables-ref` for the list of available
commands and options.
diff --git a/airflow-ctl/docs/index.rst b/airflow-ctl/docs/index.rst
new file mode 100644
index 00000000000..203cfc8845a
--- /dev/null
+++ b/airflow-ctl/docs/index.rst
@@ -0,0 +1,36 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+What is Airflow CTL®?
+=====================
+Airflow CTL is a command line tool that helps you manage and deploy Apache
Airflow environments.
+
+.. toctree::
+ :hidden:
+ :caption: Content
+
+ installation/index
+ howto/index
+ security
+ changelog
+
+.. toctree::
+ :hidden:
+ :caption: Usage
+
+ start
+ cli-and-env-variables-ref
diff --git a/airflow-ctl/docs/installation/dependencies.rst
b/airflow-ctl/docs/installation/dependencies.rst
new file mode 100644
index 00000000000..77d20b60907
--- /dev/null
+++ b/airflow-ctl/docs/installation/dependencies.rst
@@ -0,0 +1,26 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Dependencies
+------------
+
+Airflow CTL dependencies
+''''''''''''''''''''''''''
+
+The ``apache-airflow-ctl`` PyPI basic package only installs what's needed to
get started.
+Additional packages can be installed depending on what will be useful in your
+environment.
diff --git a/airflow-ctl/docs/installation/index.rst
b/airflow-ctl/docs/installation/index.rst
new file mode 100644
index 00000000000..da6c78a905a
--- /dev/null
+++ b/airflow-ctl/docs/installation/index.rst
@@ -0,0 +1,134 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+
+Installation of Airflow CTL®
+----------------------------
+
+.. contents:: :local:
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Installation
+ :hidden:
+
+ Prerequisites <prerequisites>
+ Dependencies <dependencies>
+ Supported versions <supported-versions>
+ Installing from sources <installing-from-sources>
+ Installing from PyPI <installing-from-pypi>
+
+This page describes installations options that you might use when considering
how to install Airflow®.
+Airflow consists of many components, often distributed among many physical or
virtual machines, therefore
+installation of Airflow might be quite complex, depending on the options you
choose.
+
+
+Using released sources
+''''''''''''''''''''''
+
+More details: :doc:`installing-from-sources`
+
+**When this option works best**
+
+* This option is best if you expect to build all your software from sources.
+* Apache Airflow is one of the projects that belong to the `Apache Software
Foundation <https://www.apache.org/>`__.
+ It is a requirement for all ASF projects that they can be installed using
official sources released via `Official Apache Downloads
<https://dlcdn.apache.org/>`__.
+* This is the best choice if you have a strong need to `verify the integrity
and provenance of the software <https://www.apache.org/dyn/closer.cgi#verify>`__
+
+**Intended users**
+
+* Users who are familiar with installing and building software from sources
and are conscious about integrity and provenance
+ of the software they use down to the lowest level possible.
+
+**What are you expected to handle**
+
+* You are expected to build and install airflow and its components on your own.
+* You should develop and handle the deployment for all components of Airflow.
+* You are responsible for setting up database, creating and managing database
schema with ``airflow db`` commands,
+ automated startup and recovery, maintenance, cleanup and upgrades of Airflow
and the Airflow Providers.
+* You need to setup monitoring of your system allowing you to observe
resources and react to problems.
+* You are expected to configure and manage appropriate resources for the
installation (memory, CPU, etc) based
+ on the monitoring of your installation and feedback loop. See the notes
about requirements.
+
+**What Apache Airflow Community provides for that method**
+
+* You have `instructions
<https://github.com/apache/airflow/blob/main/INSTALL>`__ on how to build the
software but due to various environments
+ and tools you might want to use, you might expect that there will be
problems which are specific to your deployment and environment
+ you will have to diagnose and solve.
+
+**Where to ask for help**
+
+* The ``#user-troubleshooting`` channel on slack can be used for quick general
troubleshooting questions. The
+ `GitHub discussions <https://github.com/apache/airflow/discussions>`__ if
you look for longer discussion and have more information to share.
+
+* The ``#user-best-practices`` channel on slack can be used to ask for and
share best practices on using and deploying airflow.
+
+* If you can provide description of a reproducible problem with Airflow
software, you can open issue at `GitHub issues
<https://github.com/apache/airflow/issues>`_
+
+* If you want to contribute back to Airflow, the ``#contributors`` slack
channel for building the Airflow itself
+
+
+Using PyPI
+'''''''''''
+
+More details: :doc:`/installation/installing-from-pypi`
+
+**When this option works best**
+
+* This installation method is useful when you are not familiar with Containers
and Docker and want to install
+ Apache Airflow on physical or virtual machines and you are used to
installing and running software using custom
+ deployment mechanism.
+
+* The only officially supported mechanism of installation is via ``pip`` using
constraint mechanisms. The constraint
+ files are managed by Apache Airflow release managers to make sure that you
can repeatably install Airflow from PyPI with all Providers and
+ required dependencies.
+
+* In case of PyPI installation you could also verify integrity and provenance
of the packages
+ downloaded from PyPI as described at the installation page, but software you
download from PyPI is pre-built
+ for you so that you can install it without building, and you do not build
the software from sources.
+
+**Intended users**
+
+* Users who are familiar with installing and configuring Python applications,
managing Python environments,
+ dependencies and running software with their custom deployment mechanisms.
+
+**What are you expected to handle**
+
+* You are expected to install Airflow CTL.
+* You should running Airflow API server.
+* You need to setup monitoring of your system allowing you to observe
resources and react to problems.
+
+**What Apache Airflow Community provides for that method**
+
+* You have :doc:`/installation/installing-from-pypi`
+ on how to install the software but due to various environments and tools you
might want to use, you might
+ expect that there will be problems which are specific to your deployment and
environment you will have to
+ diagnose and solve.
+* You have :doc:`/start` where you can see an example of Quick Start with
running Airflow
+ locally which you can use to start Airflow quickly for local testing and
development.
+ However, this is just for inspiration. Do not expect :doc:`/start` is ready
for production installation,
+ you need to build your own production-ready deployment if you follow this
approach.
+
+**Where to ask for help**
+
+* The ``#user-troubleshooting`` channel on Airflow Slack for quick general
+ troubleshooting questions. The `GitHub discussions
<https://github.com/apache/airflow/discussions>`__
+ if you look for longer discussion and have more information to share.
+* The ``#user-best-practices`` channel on slack can be used to ask for and
share best
+ practices on using and deploying airflow.
+* If you can provide description of a reproducible problem with Airflow
software, you can open
+ issue at `GitHub issues <https://github.com/apache/airflow/issues>`__
diff --git a/airflow-ctl/docs/installation/installing-from-pypi.rst
b/airflow-ctl/docs/installation/installing-from-pypi.rst
new file mode 100644
index 00000000000..f498c163298
--- /dev/null
+++ b/airflow-ctl/docs/installation/installing-from-pypi.rst
@@ -0,0 +1,163 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Installation from PyPI
+----------------------
+
+This page describes installations using the ``apache-airflow-ctl`` package
`published in
+PyPI <https://pypi.org/project/apache-airflow-ctl/>`__.
+
+Installation tools
+''''''''''''''''''
+
+Only ``pip`` installation is currently officially supported.
+
+.. note::
+
+ While there are some successes with using other tools like `poetry
<https://python-poetry.org/>`_ or
+ `pip-tools <https://pypi.org/project/pip-tools/>`_, they do not share the
same workflow as
+ ``pip`` - especially when it comes to constraint vs. requirements management.
+ Installing via ``Poetry`` or ``pip-tools`` is not currently supported. If
you wish to install airflow
+ using those tools you should use the constraints and convert them to
appropriate
+ format and workflow that your tool requires.
+
+ There are known issues with ``bazel`` that might lead to circular
dependencies when using it to install
+ Airflow. Please switch to ``pip`` if you encounter such problems. ``Bazel``
community works on fixing
+ the problem in `this PR
<https://github.com/bazelbuild/rules_python/pull/1166>`_ so it might be that
+ newer versions of ``bazel`` will handle it.
+
+Typical command to install airflowctl from scratch in a reproducible way from
PyPI looks like below:
+
+.. code-block:: bash
+
+ pip install "apache-airflow-ctl==|version|"
+
+Those are just examples, see further for more explanation why those are the
best practices.
+
+.. note::
+
+ Generally speaking, Python community established practice is to perform
application installation in a
+ virtualenv created with ``virtualenv`` or ``venv`` tools. You can also use
``uv`` or ``pipx`` to install
+ Airflow in application dedicated virtual environment created for you. There
are also other tools that can be used
+ to manage your virtualenv installation and you are free to choose how you
are managing the environments.
+ Airflow has no limitation regarding to the tool of your choice when it
comes to virtual environment.
+
+ The only exception where you might consider not using virtualenv is when
you are building a container
+ image with only Airflow installed - this is for example how Airflow is
installed in the official Container
+ image.
+
+.. _installation:constraints:
+
+Constraints files
+'''''''''''''''''
+
+Why we need constraints
+=======================
+
+Airflow CTL installation can be tricky because Airflow CTL is both a library
and an application.
+
+Libraries usually keep their dependencies open and applications usually pin
them, but we should do neither
+and both at the same time. We decided to keep our dependencies as open as
possible
+(in ``pyproject.toml``) so users can install different version of libraries if
needed. This means that
+from time to time plain ``pip install apache-airflow-ctl`` will not work or
will produce an unusable
+Airflow CTL installation.
+
+Reproducible Airflow CTL installation
+=====================================
+
+In order to have a reproducible installation, we also keep a set of constraint
files in the
+``constraints-main``, ``constraints-2-0``, ``constraints-2-1`` etc. orphan
branches and then we create a tag
+for each released version e.g. :subst-code:`constraints-|version|`.
+
+This way, we keep a tested set of dependencies at the moment of release. This
provides you with the ability
+of having the exact same installation of airflowctl + dependencies as was
known to be working
+at the moment of release - frozen set of dependencies for that version of
Airflow CTL. There is a separate
+constraints file for each version of Python that Airflow CTL supports.
+
+You can create the URL to the file substituting the variables in the template
below.
+
+.. code-block::
+
+
https://raw.githubusercontent.com/apache/airflow/airflow-ctl/constraints-${AIRFLOWCTL_VERSION}/constraints-${PYTHON_VERSION}.txt
+
+where:
+
+- ``AIRFLOW_CTL_VERSION`` - Airflow CTL version (e.g. :subst-code:`|version|`)
or ``main``, ``2-0``, for latest development version
+- ``PYTHON_VERSION`` Python version e.g. ``3.9``, ``3.10``
+
+
+Verifying installed dependencies
+================================
+
+You can also always run the ``pip check`` command to test if the set of your
Python packages is
+consistent and not conflicting.
+
+
+.. code-block:: bash
+
+ > pip check
+ No broken requirements found.
+
+
+When you see such message and the exit code from ``pip check`` is 0, you can
be sure, that there are no
+conflicting dependencies in your environment.
+
+
+Using your own constraints
+==========================
+
+When you decide to install your own dependencies, or want to upgrade or
downgrade providers, you might want
+to continue being able to keep reproducible installation of Airflow CTL and
those dependencies via a single command.
+In order to do that, you can produce your own constraints file and use it to
install Airflow CTL instead of the
+one provided by the community.
+
+.. code-block:: bash
+
+ pip install "apache-airflow-ctl==|version|"
+ pip freeze > my-constraints.txt
+
+
+Then you can use it to create reproducible installations of your environment
in a single operation via
+a local constraints file:
+
+.. code-block:: bash
+
+ pip install "apache-airflow-ctl==|version|" --constraint
"my-constraints.txt"
+
+
+Similarly as in case of Airflow CTL original constraints, you can also host
your constraints at your own
+repository or server and use it remotely from there.
+
+Fixing Constraints at release time
+''''''''''''''''''''''''''''''''''
+
+The released "versioned" constraints are mostly ``fixed`` when we release
Airflow CTL version and we only
+update them in exceptional circumstances. For example when we find out that
the released constraints might prevent
+Airflow CTL from being installed consistently from the scratch.
+
+In normal circumstances, the constraint files are not going to change if new
version of Airflow CTL
+dependencies are released - not even when those versions contain critical
security fixes.
+The process of Airflow CTL releases is designed around upgrading dependencies
automatically where
+applicable but only when we release a new version of Airflow CTL, not for
already released versions.
+
+Between the releases you can upgrade dependencies on your own and you can keep
your own constraints
+updated as described in the previous section.
+
+The easiest way to keep-up with the latest released dependencies is to upgrade
to the latest released
+Airflow CTL version. Whenever we release a new version of Airflow CTL, we
upgrade all dependencies to the latest
+applicable versions and test them together, so if you want to keep up with
those tests - staying up-to-date
+with latest version of Airflow CTL is the easiest way to update those
dependencies.
diff --git a/airflow-ctl/docs/installation/installing-from-sources.rst
b/airflow-ctl/docs/installation/installing-from-sources.rst
new file mode 100644
index 00000000000..41b52763d55
--- /dev/null
+++ b/airflow-ctl/docs/installation/installing-from-sources.rst
@@ -0,0 +1,163 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Installing from Sources
+-----------------------
+
+Released packages
+'''''''''''''''''
+
+.. jinja:: official_download_page
+
+ This page describes downloading and verifying Airflow® version
+ ``{{ airflow_version }}`` using officially released packages.
+ You can also install ``Apache Airflow CTL`` - as most Python packages -
via :doc:`PyPI <installing-from-pypi>`.
+ You can choose different version of Airflow by selecting a different
version from the drop-down at
+ the top-left of the page.
+
+The ``source``, ``sdist`` and ``whl`` packages released are the "official"
sources of installation that you
+can use if you want to verify the origin of the packages and want to verify
checksums and signatures of
+the packages. The packages are available via the
+`Official Apache Software Foundations Downloads <https://dlcdn.apache.org/>`_
+
+As of version 2.8 Airflow follows PEP 517/518 and uses ``pyproject.toml`` file
to define build dependencies
+and build process and it requires relatively modern versions of packaging
tools to get airflow built from
+local sources or ``sdist`` packages, as PEP 517 compliant build hooks are used
to determine dynamic build
+dependencies. In case of ``pip`` it means that at least version 22.1.0 is
needed (released at the beginning of
+2022) to build or install Airflow from sources. This does not affect the
ability of installing Airflow from
+released wheel packages.
+
+The |version| downloads of Airflow CTL are available at:
+
+.. jinja:: official_download_page
+
+ * `Sources package for airflow <{{ closer_lua_url }}/apache-airflow-ctl-{{
airflowctl_version }}-source.tar.gz>`__ (`asc <{{ base_url
}}/apache-airflow-ctl-{{ airflowctl_version }}-source.tar.gz.asc>`__, `sha512
<{{ base_url }}/apache-airflow-ctl-{{ airflowctl_version
}}-source.tar.gz.sha512>`__)
+ * `Sdist package for airflow meta package <{{ closer_lua_url
}}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz>`__ (`asc <{{ base_url
}}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz.asc>`__, `sha512 <{{
base_url }}/apache-airflow-ctl-{{ airflowctl_version }}.tar.gz.sha512>`__)
+ * `Whl package for airflow meta package <{{ closer_lua_url
}}/apache_airflow_ctl-{{ airflowctl_version }}-py3-none-any.whl>`__ (`asc <{{
base_url }}/apache_airflow_ctl-{{ airflowctl_version
}}-py3-none-any.whl.asc>`__, `sha512 <{{ base_url }}/apache_airflow_ctl-{{
airflowctl_version }}-py3-none-any.whl.sha512>`__)
+ * `Sdist package for airflow core package <{{ closer_lua_url
}}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz>`__ (`asc <{{ base_url
}}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz.asc>`__, `sha512 <{{
base_url }}/apache-airflow_ctl-{{ airflowctl_version }}.tar.gz.sha512>`__)
+ * `Whl package for airflow core package <{{ closer_lua_url
}}/apache_airflow_ctl-{{ airflowctl_version }}-py3-none-any.whl>`__ (`asc <{{
base_url }}/apache_airflow_ctl-{{ airflowctl_version
}}-py3-none-any.whl.asc>`__, `sha512 <{{ base_url }}/apache_airflow_ctl-{{
airflowctl_version }}-py3-none-any.whl.sha512>`__)
+
+If you want to install from the source code, you can download from the sources
link above, it will contain
+a ``INSTALL`` file containing details on how you can build and install Airflow
CTL.
+
+Release integrity
+'''''''''''''''''
+
+`PGP signatures KEYS <https://downloads.apache.org/airflowctl/KEYS>`__
+
+It is essential that you verify the integrity of the downloaded files using
the PGP or SHA signatures.
+The PGP signatures can be verified using GPG or PGP. Please download the KEYS
as well as the asc
+signature files for relevant distribution. It is recommended to get these
files from the
+main distribution directory and not from the mirrors.
+
+.. code-block:: bash
+
+ gpg -i KEYS
+
+or
+
+.. code-block:: bash
+
+ pgpk -a KEYS
+
+or
+
+.. code-block:: bash
+
+ pgp -ka KEYS
+
+To verify the binaries/sources you can download the relevant asc files for it
from main
+distribution directory and follow the below guide.
+
+.. code-block:: bash
+
+ gpg --verify apache-airflow-ctl-********.asc apache-airflow-ctl-*********
+
+or
+
+.. code-block:: bash
+
+ pgpv apache-airflow-ctl-********.asc
+
+or
+
+.. code-block:: bash
+
+ pgp apache-airflow-********.asc
+
+Example:
+
+.. code-block:: console
+ :substitutions:
+
+ $ gpg --verify apache-airflow-ctl-|version|-source.tar.gz.asc
apache-airflow-ctl-|version|-source.tar.gz
+ gpg: Signature made Sat 11 Sep 12:49:54 2021 BST
+ gpg: using RSA key
CDE15C6E4D3A8EC4ECF4BA4B6674E08AD7DE406F
+ gpg: issuer "[email protected]"
+ gpg: Good signature from "Kaxil Naik <[email protected]>" [unknown]
+ gpg: aka "Kaxil Naik <[email protected]>" [unknown]
+ gpg: WARNING: The key's User ID is not certified with a trusted
signature!
+ gpg: There is no indication that the signature belongs to the
owner.
+ Primary key fingerprint: CDE1 5C6E 4D3A 8EC4 ECF4 BA4B 6674 E08A D7DE
406F
+
+The "Good signature from ..." is indication that the signatures are correct.
+Do not worry about the "not certified with a trusted signature" warning. Most
of the certificates used
+by release managers are self signed, that's why you get this warning. By
importing the server in the
+previous step and importing it via ID from ``KEYS`` page, you know that this
is a valid Key already.
+
+For SHA512 sum check, download the relevant ``sha512`` and run the following:
+
+.. code-block:: bash
+
+ shasum -a 512 apache-airflow-ctl--******** | diff -
apache-airflow-ctl--********.sha512
+
+The ``SHASUM`` of the file should match the one provided in ``.sha512`` file.
+
+Example:
+
+.. code-block:: bash
+ :substitutions:
+
+ shasum -a 512 apache-airflow-ctl-|version|-source.tar.gz | diff -
apache-airflow-ctl-|version|-source.tar.gz.sha512
+
+
+Verifying PyPI releases
+'''''''''''''''''''''''
+
+You can verify the Airflow CTL ``.whl`` packages from PyPI by locally
downloading the package and signature
+and SHA sum files with the script below:
+
+
+.. jinja:: official_download_page
+
+ .. code-block:: bash
+
+ #!/bin/bash
+ airflowctl_version="{{ airflowctl_version }}"
+ ctl_download_dir="$(mktemp -d)"
+ pip download --no-deps "apache-airflow-ctl==${airflowctl_version}"
--dest "${airflow_download_dir}"
+ curl
"https://downloads.apache.org/airflowctl/${airflowctl_version}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.asc"
\
+ -L -o
"${airflowctl_download_dir}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.asc"
+ curl
"https://downloads.apache.org/airflow/${airflowctl_version}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.sha512"
\
+ -L -o
"${airflowctl_download_dir}/apache_airflow_ctl-${airflowctl_version}-py3-none-any.whl.sha512"
+ echo
+ echo "Please verify files downloaded to ${airflowctl_download_dir}"
+ ls -la "${airflowctl_download_dir}"
+ echo
+
+Once you verify the files following the instructions from previous chapter you
can remove the temporary
+folder created.
diff --git a/airflow-ctl/docs/installation/prerequisites.rst
b/airflow-ctl/docs/installation/prerequisites.rst
new file mode 100644
index 00000000000..615558e450c
--- /dev/null
+++ b/airflow-ctl/docs/installation/prerequisites.rst
@@ -0,0 +1,63 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Prerequisites
+-------------
+
+Airflow CTL is tested with:
+
+
+The minimum memory required we recommend Airflow CTL to run with is 4GB, but
the actual requirements depend
+wildly on the deployment options you have.
+The Keyring backend needs to be installed separately into your operating
system. This will enhance security. See :doc:`/security` for more information.
+
+Keyring Backend
+'''''''''''''''
+Airflow CTL uses keyring to store the API token securely. This ensures that
the token is not stored in plain text and is only accessible to authorized
users.
+
+Recommended keyring backends are:
+* `macOS Keychain <https://en.wikipedia.org/wiki/Keychain_%28software%29>`_
+* `Freedesktop Secret Service
<http://standards.freedesktop.org/secret-service/>`_ supports many DE including
GNOME (requires `secretstorage <https://pypi.python.org/pypi/secretstorage>`_)
+* `KDE4 & KDE5 KWallet <https://en.wikipedia.org/wiki/KWallet>`_ (requires
`dbus <https://pypi.python.org/pypi/dbus-python>`_)
+* `Windows Credential Locker
<https://docs.microsoft.com/en-us/windows/uwp/security/credential-locker>`_
+
+Third-Party Backends
+====================
+
+In addition to the backends provided by the core keyring package for
+the most common and secure use cases, there
+are additional keyring backend implementations available for other
+use cases. Simply install them to make them available:
+
+- `keyrings.cryptfile <https://pypi.org/project/keyrings.cryptfile>`_
+ - Encrypted text file storage.
+- `keyrings.alt <https://pypi.org/project/keyrings.alt>`_
+ - "alternate", possibly-insecure backends, originally part of the core
package, but available for opt-in.
+- `gsheet-keyring <https://pypi.org/project/gsheet-keyring>`_
+ - a backend that stores secrets in a Google Sheet. For use with
`ipython-secrets <https://pypi.org/project/ipython-secrets>`_.
+- `bitwarden-keyring <https://pypi.org/project/bitwarden-keyring/>`_
+ - a backend that stores secrets in the `BitWarden
<https://bitwarden.com/>`_ password manager.
+- `onepassword-keyring <https://pypi.org/project/onepassword-keyring/>`_
+ - a backend that stores secrets in the `1Password
<https://1password.com/>`_ password manager.
+- `sagecipher <https://pypi.org/project/sagecipher>`_
+ - an encryption backend which uses the ssh agent protocol's signature
operation to derive the cipher key.
+- `keyrings.osx_keychain_keys
<https://pypi.org/project/keyrings.osx-keychain-keys>`_
+ - ``OSX keychain key-management``, for private, public, and symmetric keys.
+- `keyring_pass.PasswordStoreBackend
<https://github.com/nazarewk/keyring_pass>`_
+ - Password Store (pass) backend for python's keyring
+- `keyring_jeepney <https://pypi.org/project/keyring_jeepney>`__
+ - a pure Python backend using the secret service ``DBus`` API for desktop
Linux (requires ``keyring<24``).
diff --git a/airflow-ctl/docs/installation/supported-versions.rst
b/airflow-ctl/docs/installation/supported-versions.rst
new file mode 100644
index 00000000000..823d4e5b461
--- /dev/null
+++ b/airflow-ctl/docs/installation/supported-versions.rst
@@ -0,0 +1,42 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Supported versions
+------------------
+
+Version Life Cycle
+``````````````````
+
+Apache Airflow CTL is compatible with Apache Airflow® versions 3.0.0 and later.
+
+Apache Airflow CTL version life cycle:
+
+ .. This table is automatically updated by pre-commit
scripts/ci/pre_commit/supported_versions.py
+ .. Beginning of auto-generated table
+
+========= ===================== ========= ===============
================= ================
+Version Current Patch/Minor State First Release Limited Support
EOL/Terminated
+========= ===================== ========= ===============
================= ================
+1 1.0.0 Supported TBD TBD
TBD
+========= ===================== ========= ===============
================= ================
+
+ .. End of auto-generated table
+
+
+Limited support versions will be supported with security and critical bug fix
only.
+EOL versions will not get any fixes nor support.
+We **highly** recommend installing the latest Airflow CTL release which has
richer features.
diff --git a/airflow-ctl/src/airflowctl/__init__.py
b/airflow-ctl/docs/redirects.txt
similarity index 81%
copy from airflow-ctl/src/airflowctl/__init__.py
copy to airflow-ctl/docs/redirects.txt
index 2782fd09001..93082af1472 100644
--- a/airflow-ctl/src/airflowctl/__init__.py
+++ b/airflow-ctl/docs/redirects.txt
@@ -1,4 +1,3 @@
-#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -15,8 +14,14 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-from __future__ import annotations
-__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type:
ignore
+# References
+cli-ref.rst cli-and-env-variables-ref.rst
+Quick Start start.rst
+changelog.rst changelog.rst
+
+# Installation.rst
+installation/index.rst
-__version__ = "0.0.1"
+# How to
+howto/index.rst
diff --git a/airflow-ctl/docs/security.rst b/airflow-ctl/docs/security.rst
new file mode 100644
index 00000000000..1e83537a7a5
--- /dev/null
+++ b/airflow-ctl/docs/security.rst
@@ -0,0 +1,27 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Security
+========
+Airflow CTL is leveraging Apache Airflow Public API security features and
additional layers of security to ensure that your data is safe and secure.
+Airflow CTL facilitates the seamless deployment of CLI and API features
together, reducing redundancy and simplifying maintenance. Transitioning from
direct database access to an API-driven model will enhance the CLI's
capabilities and improve security.
+
+- **Authentication**: Airflow CTL uses authentication to ensure that only
authorized users can access the system. This is done using an API token. See
more on https://airflow.apache.org/docs/apache-airflow/stable/security/api.html
+
+- **Keyring**: Airflow CTL uses keyring to store the API token securely. This
ensures that the token is not stored in plain text and is only accessible to
authorized users.
+
+Airflow CTL API Token has its own expiration time. The default is 1 hour. You
can change it in the Airflow configuration file (airflow.cfg) by setting the
``jwt_cli_expiration_time`` parameter under the ``[api_auth]`` section. The
value is in seconds. This will impact all users using ``airflowctl``.
diff --git a/airflow-ctl/docs/start.rst b/airflow-ctl/docs/start.rst
new file mode 100644
index 00000000000..523e7696b6d
--- /dev/null
+++ b/airflow-ctl/docs/start.rst
@@ -0,0 +1,42 @@
+ .. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ .. http://www.apache.org/licenses/LICENSE-2.0
+
+ .. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Quick Start
+-----------
+
+Airflow CTL is a command line tool that helps you manage your Airflow
deployments.
+It is designed to be easy to use and provides a simple interface for managing
your Airflow environment.
+
+To get started, you can use the following command to create a new Airflow CTL
environment:
+
+.. code-block:: bash
+
+ airflowctl auth login --username <username> --password <password> --api-url
<api_url> --env <env_name>
+
+OR
+
+.. code-block:: bash
+
+ export AIRFLOW_CLI_TOKEN=<token>
+ airflowctl auth login --api-url <api_url> --env <env_name>
+
+This command will create a new Airflow CTL environment with the specified
username and password.
+You can then use the following command to start the Airflow CTL environment:
+
+.. code-block:: bash
+
+ airflowctl --help
diff --git a/airflow-ctl/docs/static/exampleinclude.css
b/airflow-ctl/docs/static/exampleinclude.css
new file mode 100644
index 00000000000..b4f2a42dcc7
--- /dev/null
+++ b/airflow-ctl/docs/static/exampleinclude.css
@@ -0,0 +1,86 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+.example-header {
+ position: relative;
+ background: #9aaa7a;
+ padding: 8px 16px;
+ margin-bottom: 0;
+}
+
+.example-header--with-button {
+ padding-right: 166px;
+}
+
+.example-header::after {
+ content: '';
+ display: table;
+ clear: both;
+}
+
+.example-title {
+ display: block;
+ padding: 4px;
+ margin-right: 16px;
+ color: #fff;
+ overflow-x: auto;
+}
+
+.example-header-button {
+ top: 8px;
+ right: 16px;
+ position: absolute;
+}
+
+.example-header + .highlight-python {
+ margin-top: 0 !important;
+}
+
+.viewcode-button {
+ display: inline-block;
+ padding: 8px 16px;
+ border: 0;
+ margin: 0;
+ outline: 0;
+ border-radius: 2px;
+ box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.3);
+ color: #404040;
+ background-color: #e7e7e7;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 1;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ text-transform: uppercase;
+ transition: background-color 0.2s;
+ vertical-align: middle;
+ white-space: nowrap;
+}
+
+.viewcode-button:visited {
+ color: #404040;
+}
+
+.viewcode-button:hover,
+.viewcode-button:focus {
+ color: #404040;
+ background-color: #d6d6d6;
+}
diff --git a/airflow-ctl/docs/static/gh-jira-links.js
b/airflow-ctl/docs/static/gh-jira-links.js
new file mode 100644
index 00000000000..dfbfb15ad88
--- /dev/null
+++ b/airflow-ctl/docs/static/gh-jira-links.js
@@ -0,0 +1,36 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+document.addEventListener('DOMContentLoaded', function() {
+ var el = document.getElementById('release-notes');
+ if (el !== null ) {
+ // [AIRFLOW-...]
+ el.innerHTML = el.innerHTML.replace(
+ /\[(AIRFLOW-[\d]+)\]/g,
+ `<a href="https://issues.apache.org/jira/browse/$1">[$1]</a>`
+ );
+ // (#...)
+ el.innerHTML = el.innerHTML.replace(
+ /#([\d]+)/g,
+ (match, group1) => {
+ return `<a
href="https://github.com/apache/airflow/pull/${group1}">#${group1}</a>`
+ }
+ );
+ };
+})
diff --git a/airflow-ctl/docs/static/redirects.js
b/airflow-ctl/docs/static/redirects.js
new file mode 100644
index 00000000000..4b44336e440
--- /dev/null
+++ b/airflow-ctl/docs/static/redirects.js
@@ -0,0 +1,29 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+document.addEventListener("DOMContentLoaded", function () {
+ const redirects = {
+ "zombie-undead-tasks": "task-instance-heartbeat-timeout",
+ "zombie-tasks": "task-instance-heartbeat-timeout",
+ };
+ const fragment = window.location.hash.substring(1);
+ if (redirects[fragment]) {
+ window.location.hash = redirects[fragment];
+ }
+});
diff --git a/airflow-ctl/docs/templates/footer.html
b/airflow-ctl/docs/templates/footer.html
new file mode 100644
index 00000000000..10c1500f3ee
--- /dev/null
+++ b/airflow-ctl/docs/templates/footer.html
@@ -0,0 +1,29 @@
+{#
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+#}
+
+{% extends "!footer.html" %}
+
+{% block extrafooter %}
+ <div class="footer">This page uses <a href="https://analytics.google.com/">
+ Google Analytics</a> to collect statistics. You can disable it by blocking
+ the JavaScript coming from www.google-analytics.com. Check our
+ <a href="{{ pathto('privacy_notice') }}">Privacy Policy</a>
+ for more details.
+ </div>
+{% endblock %}
diff --git a/airflow-ctl/pyproject.toml b/airflow-ctl/pyproject.toml
index 3a4796a32a9..8eeac9ed8b3 100644
--- a/airflow-ctl/pyproject.toml
+++ b/airflow-ctl/pyproject.toml
@@ -96,6 +96,21 @@ exclude_also = [
]
[dependency-groups]
+# To build docs:
+#
+# uv run --group docs build-docs
+#
+# To enable auto-refreshing build with server:
+#
+# uv run --group docs build-docs --autobuild
+#
+# To see more options:
+#
+# uv run --group docs build-docs --help
+#
+docs = [
+ "apache-airflow-devel-common[docs]"
+]
dev = [
"apache-airflow-ctl[dev]",
]
@@ -142,7 +157,6 @@ addopts = [
"--asyncio-mode=strict",
]
-
norecursedirs = [
".eggs",
]
@@ -164,3 +178,9 @@ pythonpath = "tests"
# Keep temporary directories (created by `tmp_path`) for 2 recent runs only
failed tests.
tmp_path_retention_count = "2"
tmp_path_retention_policy = "failed"
+
+[tool.uv]
+required-version = ">=0.6.3"
+
+[tool.uv.sources]
+apache-airflow-devel-common = { workspace = true }
diff --git a/airflow-ctl/src/airflowctl/__init__.py
b/airflow-ctl/src/airflowctl/__init__.py
index 2782fd09001..82f4e830b6a 100644
--- a/airflow-ctl/src/airflowctl/__init__.py
+++ b/airflow-ctl/src/airflowctl/__init__.py
@@ -19,4 +19,4 @@ from __future__ import annotations
__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type:
ignore
-__version__ = "0.0.1"
+__version__ = "1.0.0"
diff --git a/airflow-ctl/src/airflowctl/api/operations.py
b/airflow-ctl/src/airflowctl/api/operations.py
index ed015a439d9..20b10a0a92f 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -57,6 +57,7 @@ from airflowctl.api.datamodels.generated import (
VariableResponse,
VersionInfo,
)
+from airflowctl.exceptions import AirflowCtlConnectionException
if TYPE_CHECKING:
from airflowctl.api.client import Client
@@ -104,7 +105,9 @@ def _check_flag_and_exit_if_server_response_error(func):
return _exit_if_server_response_error(response=func(self,
*args, **kwargs))
return func(self, *args, **kwargs)
except httpx.ConnectError as e:
- raise e
+ if "Connection refused" in str(e):
+ raise AirflowCtlConnectionException("Connection refused. Is
the API server running?")
+ raise AirflowCtlConnectionException(f"Connection error: {e}")
return wrapped
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index c8bce27b368..7e56fc61281 100644
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -36,7 +36,11 @@ import rich
import airflowctl.api.datamodels.generated as generated_datamodels
from airflowctl.api.client import NEW_API_CLIENT, Client, ClientKind,
provide_api_client
from airflowctl.api.operations import BaseOperations, ServerResponseError
-from airflowctl.exceptions import AirflowCtlCredentialNotFoundException,
AirflowCtlNotFoundException
+from airflowctl.exceptions import (
+ AirflowCtlConnectionException,
+ AirflowCtlCredentialNotFoundException,
+ AirflowCtlNotFoundException,
+)
from airflowctl.utils.module_loading import import_string
BUILD_DOCS = "BUILDING_AIRFLOW_DOCS" in os.environ
@@ -60,6 +64,8 @@ def safe_call_command(function: Callable, args:
Iterable[Arg]) -> None:
function(args)
except AirflowCtlCredentialNotFoundException as e:
rich.print(f"command failed due to {e}")
+ except AirflowCtlConnectionException as e:
+ rich.print(f"command failed due to {e}")
except AirflowCtlNotFoundException as e:
rich.print(f"command failed due to {e}")
@@ -216,7 +222,28 @@ class GroupCommand(NamedTuple):
epilog: str | None = None
-CLICommand = Union[ActionCommand, GroupCommand]
+class GroupCommandParser(NamedTuple):
+ """ClI command with subcommands."""
+
+ name: str
+ help: str
+ subcommands: Iterable
+ description: str | None = None
+ epilog: str | None = None
+
+ @classmethod
+ def from_group_command(cls, group_command: GroupCommand) ->
GroupCommandParser:
+ """Create GroupCommandParser from GroupCommand."""
+ return cls(
+ name=group_command.name,
+ help=group_command.help,
+ subcommands=group_command.subcommands,
+ description=group_command.description,
+ epilog=group_command.epilog,
+ )
+
+
+CLICommand = Union[ActionCommand, GroupCommand, GroupCommandParser]
class CommandFactory:
@@ -355,7 +382,7 @@ class CommandFactory:
arg_flags=("--" +
self._sanitize_arg_parameter_key(field),),
arg_type=field_type.annotation,
arg_action=argparse.BooleanOptionalAction if
field_type.annotation is bool else None, # type: ignore
- arg_help=f"Argument Type: {field_type.annotation},
{field} for {parameter_key} operation",
+ arg_help=f"{field} for {parameter_key} operation",
arg_default=False if field_type.annotation is bool
else None,
)
)
@@ -370,7 +397,7 @@ class CommandFactory:
arg_flags=("--" +
self._sanitize_arg_parameter_key(field),),
arg_type=annotation,
arg_action=argparse.BooleanOptionalAction if
annotation is bool else None, # type: ignore
- arg_help=f"Argument Type: {annotation}, {field} for
{parameter_key} operation",
+ arg_help=f"{field} for {parameter_key} operation",
arg_default=False if annotation is bool else None,
)
)
@@ -390,7 +417,7 @@ class CommandFactory:
arg_action=argparse.BooleanOptionalAction
if type(parameter_type) is bool
else None,
- arg_help=f"Argument Type:
{type(parameter_type)}, {parameter_key} for {operation.get('name')} operation
in {operation.get('parent').name}",
+ arg_help=f"{parameter_key} for
{operation.get('name')} operation in {operation.get('parent').name}",
arg_default=False if type(parameter_type) is
bool else None,
)
)
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_parser.py
b/airflow-ctl/src/airflowctl/ctl/cli_parser.py
index 685d46322b4..138f714462a 100644
--- a/airflow-ctl/src/airflowctl/ctl/cli_parser.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_parser.py
@@ -38,6 +38,7 @@ from airflowctl.ctl.cli_config import (
ActionCommand,
DefaultHelpParser,
GroupCommand,
+ GroupCommandParser,
core_commands,
)
from airflowctl.exceptions import AirflowCtlException
@@ -54,7 +55,10 @@ airflow_commands = core_commands.copy() # make a copy to
prevent bad interactio
log = logging.getLogger(__name__)
-ALL_COMMANDS_DICT: dict[str, CLICommand] = {sp.name: sp for sp in
airflow_commands}
+ALL_COMMANDS_DICT: dict[str, CLICommand] = {
+ sp.name: GroupCommandParser.from_group_command(sp) if isinstance(sp,
GroupCommand) else sp
+ for sp in airflow_commands
+}
class AirflowHelpFormatter(RichHelpFormatter):
@@ -69,7 +73,7 @@ class AirflowHelpFormatter(RichHelpFormatter):
self._indent()
subactions = action._get_subactions()
action_subcommands, group_subcommands = partition(
- lambda d: isinstance(ALL_COMMANDS_DICT[d.dest], GroupCommand),
subactions
+ lambda d: isinstance(ALL_COMMANDS_DICT[d.dest],
GroupCommandParser), subactions
)
yield Action([], f"\n{' ':{self._current_indent}}Groups", nargs=0)
self._indent()
@@ -131,7 +135,7 @@ def _add_command(subparsers: argparse._SubParsersAction,
sub: CLICommand) -> Non
)
sub_proc.formatter_class = LazyRichHelpFormatter
- if isinstance(sub, GroupCommand):
+ if isinstance(sub, GroupCommandParser):
_add_group_command(sub, sub_proc)
elif isinstance(sub, ActionCommand):
_add_action_command(sub, sub_proc)
@@ -145,7 +149,7 @@ def _add_action_command(sub: ActionCommand, sub_proc:
argparse.ArgumentParser) -
sub_proc.set_defaults(func=sub.func)
-def _add_group_command(sub: GroupCommand, sub_proc: argparse.ArgumentParser)
-> None:
+def _add_group_command(sub: GroupCommandParser, sub_proc:
argparse.ArgumentParser) -> None:
subcommands = sub.subcommands
sub_subparsers = sub_proc.add_subparsers(dest="subcommand",
metavar="COMMAND")
sub_subparsers.required = True
diff --git a/airflow-ctl/src/airflowctl/exceptions.py
b/airflow-ctl/src/airflowctl/exceptions.py
index b313e969b2c..515879ec359 100644
--- a/airflow-ctl/src/airflowctl/exceptions.py
+++ b/airflow-ctl/src/airflowctl/exceptions.py
@@ -36,3 +36,7 @@ class AirflowCtlNotFoundException(AirflowCtlException):
class AirflowCtlCredentialNotFoundException(AirflowCtlNotFoundException):
"""Raise when a credential couldn't be found while performing an
operation."""
+
+
+class AirflowCtlConnectionException(AirflowCtlException):
+ """Raise when a connection error occurs while performing an operation."""
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index a722d15369e..631e32bd60a 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -20,8 +20,6 @@ from __future__ import annotations
import datetime
import json
import uuid
-from contextlib import redirect_stdout
-from io import StringIO
from typing import TYPE_CHECKING
import httpx
@@ -75,6 +73,7 @@ from airflowctl.api.datamodels.generated import (
VariableResponse,
VersionInfo,
)
+from airflowctl.exceptions import AirflowCtlConnectionException
if TYPE_CHECKING:
from pydantic import NonNegativeInt
@@ -93,13 +92,10 @@ def make_api_client(
class TestBaseOperations:
def test_server_connection_refused(self):
client = make_api_client(base_url="http://localhost")
- with (
- pytest.raises(httpx.ConnectError),
- redirect_stdout(StringIO()) as stdout,
+ with pytest.raises(
+ AirflowCtlConnectionException, match="Connection refused. Is the
API server running?"
):
- client.connections.get(1)
- stdout = stdout.getvalue()
- assert "" in stdout
+ client.connections.get("1")
class TestAssetsOperations:
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
index 94073d8350f..fe9ed6bad0a 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
@@ -46,7 +46,7 @@ def test_args():
(
"--dag-id",
{
- "help": "Argument Type: <class 'str'>, dag_id for backfill
operation",
+ "help": "dag_id for backfill operation",
"action": None,
"default": None,
"type": str,
@@ -56,7 +56,7 @@ def test_args():
(
"--from-date",
{
- "help": "Argument Type: <class 'datetime.datetime'>, from_date
for backfill operation",
+ "help": "from_date for backfill operation",
"action": None,
"default": None,
"type": datetime.datetime,
@@ -66,7 +66,7 @@ def test_args():
(
"--to-date",
{
- "help": "Argument Type: <class 'datetime.datetime'>, to_date
for backfill operation",
+ "help": "to_date for backfill operation",
"action": None,
"default": None,
"type": datetime.datetime,
@@ -76,7 +76,7 @@ def test_args():
(
"--run-backwards",
{
- "help": "Argument Type: <class 'bool'>, run_backwards for
backfill operation",
+ "help": "run_backwards for backfill operation",
"action": BooleanOptionalAction,
"default": False,
"type": bool,
@@ -86,7 +86,7 @@ def test_args():
(
"--dag-run-conf",
{
- "help": "Argument Type: dict[str, typing.Any], dag_run_conf
for backfill operation",
+ "help": "dag_run_conf for backfill operation",
"action": None,
"default": None,
"type": dict[str, Any],
@@ -96,7 +96,7 @@ def test_args():
(
"--reprocess-behavior",
{
- "help": "Argument Type: <enum 'ReprocessBehavior'>,
reprocess_behavior for backfill operation",
+ "help": "reprocess_behavior for backfill operation",
"action": None,
"default": None,
"type": ReprocessBehavior,
@@ -106,7 +106,7 @@ def test_args():
(
"--max-active-runs",
{
- "help": "Argument Type: <class 'int'>, max_active_runs for
backfill operation",
+ "help": "max_active_runs for backfill operation",
"action": None,
"default": None,
"type": int,
diff --git a/devel-common/src/docs/utils/conf_constants.py
b/devel-common/src/docs/utils/conf_constants.py
index a6699b25cee..3a92e385a8b 100644
--- a/devel-common/src/docs/utils/conf_constants.py
+++ b/devel-common/src/docs/utils/conf_constants.py
@@ -45,6 +45,11 @@ AIRFLOW_CORE_DOCKER_COMPOSE_PATH = AIRFLOW_CORE_DOCS_PATH /
"howto" / "docker-co
AIRFLOW_CORE_SRC_PATH = AIRFLOW_CORE_ROOT_PATH / "src"
AIRFLOW_FAVICON_PATH = AIRFLOW_CORE_SRC_PATH / "airflow" / "ui" / "public" /
"pin_32.png"
+AIRFLOW_CTL_ROOT_PATH = AIRFLOW_REPO_ROOT_PATH / "airflow-ctl"
+AIRFLOW_CTL_DOCS_PATH = AIRFLOW_CTL_ROOT_PATH / "docs"
+AIRFLOW_CTL_DOC_STATIC_PATH = AIRFLOW_CTL_DOCS_PATH / "static"
+AIRFLOW_CTL_SRC_PATH = AIRFLOW_CTL_ROOT_PATH / "src"
+
CHART_PATH = AIRFLOW_CORE_ROOT_PATH / "chart"
CHART_DOC_PATH = AIRFLOW_CORE_DOCS_PATH / "docs"
diff --git a/devel-common/src/sphinx_exts/docs_build/docs_builder.py
b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
index 44ba83e6f7a..59b3692c695 100644
--- a/devel-common/src/sphinx_exts/docs_build/docs_builder.py
+++ b/devel-common/src/sphinx_exts/docs_build/docs_builder.py
@@ -65,6 +65,8 @@ class AirflowDocsBuilder:
self.is_docker_stack = True
if self.package_name == "apache-airflow-providers":
self.is_providers_summary = True
+ if self.package_name == "apache-airflow-ctl":
+ self.is_airflow_ctl = True
@property
def _doctree_dir(self) -> Path:
@@ -117,6 +119,8 @@ class AirflowDocsBuilder:
if self.package_name.startswith("apache-airflow-providers-"):
package_paths = self.package_name[len("apache-airflow-providers-")
:].split("-")
return (AIRFLOW_CONTENT_ROOT_PATH /
"providers").joinpath(*package_paths) / "docs"
+ if self.package_name == "apache-airflow-ctl":
+ return AIRFLOW_CONTENT_ROOT_PATH / "airflow-ctl" / "docs"
console.print(f"[red]Unknown package name: {self.package_name}")
sys.exit(1)
@@ -330,6 +334,7 @@ def get_available_packages(include_suspended: bool = False,
short_form: bool = F
"apache-airflow",
*provider_names,
"apache-airflow-providers",
+ "apache-airflow-ctl",
"helm-chart",
"docker-stack",
]