Skip to content

Commit 4507a52

Browse files
authored
feat: Add GitHub CI/CD versioning support with env profile and Bearer tokens (#393)
Add native support for NiFi's GitHub Flow Registry Client to enable CI/CD workflows for versioned NiFi flows. Git-specific versioning helpers: - list_git_registry_buckets, get_git_registry_bucket - list_git_registry_flows, get_git_registry_flow - list_git_registry_flow_versions, deploy_git_registry_flow - update_git_flow_ver with revert_flow_ver wait param - ensure_registry_client, update_registry_client Profile system enhancements: - Add 'env' profile for pure environment variable configuration - Add bearer token authentication (nifi_bearer_token, NIFI_BEARER_TOKEN) - Ideal for GitHub Actions, containers, and CI/CD pipelines Canvas improvements: - Add name param and auto_stop to update_processor - Add schedule_all_controllers for bulk enable/disable operations - revert_flow_ver now refreshes revision internally Infrastructure: - Add SANs and cert config for github-cicd profile - Update pylintrc for pylint 3.x compatibility - Add GH_REGISTRY_TOKEN to coverage job Bug fixes: - Fix test_create_controller leaving orphaned controller services Documentation: - Add 1.1.0 release notes and env/bearer-token profile guides Related: nipyapi-actions and nipyapi-workflow companion repositories
1 parent 107e324 commit 4507a52

File tree

20 files changed

+2590
-703
lines changed

20 files changed

+2590
-703
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
run: make wait-ready NIPYAPI_PROFILE=single-user
4040

4141
- name: Run integration tests with coverage
42+
env:
43+
GH_REGISTRY_TOKEN: ${{ secrets.GH_REGISTRY_TOKEN }}
4244
run: NIPYAPI_PROFILE=single-user PYTHONPATH=${{ github.workspace }}:$PYTHONPATH pytest --cov=nipyapi --cov-report=xml --cov-report=term-missing
4345

4446
- name: Upload coverage to Codecov

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,4 @@ htmlcov/
190190

191191
# setuptools-scm generated version file
192192
nipyapi/_version.py
193+
.cursor/

Makefile

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
# Default NiFi/Registry version for docker compose profiles
55
NIFI_VERSION ?= 2.6.0
66

7+
# Load .env file if it exists (for secrets like GH_REGISTRY_TOKEN)
8+
-include .env
9+
export
10+
711
# Python command for cross-platform compatibility
812
# Defaults to 'python' for conda/venv users, override with PYTHON=python3 for system installs
913
PYTHON ?= python
@@ -227,17 +231,17 @@ extract-jks: ## extract PEM certificates from JKS: make extract-jks JKS_FILE=tru
227231
fi
228232
@echo "✅ Certificates extracted to resources/certs/extracted/"
229233

230-
up: ensure-certs # bring up docker profile: make up NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc (uses NIFI_VERSION=$(NIFI_VERSION))
231-
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc)"; exit 1; fi
234+
up: ensure-certs # bring up docker profile: make up NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd (uses NIFI_VERSION=$(NIFI_VERSION))
235+
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd)"; exit 1; fi
232236
$(DC) --profile $(NIPYAPI_PROFILE) up -d
233237

234238
down: ## bring down all docker services
235239
@echo "Bringing down Docker services (NIFI_VERSION=$(NIFI_VERSION))"
236-
@$(DC) --profile single-user --profile secure-ldap --profile secure-mtls --profile secure-oidc down -v --remove-orphans || true
240+
@$(DC) --profile single-user --profile secure-ldap --profile secure-mtls --profile secure-oidc --profile github-cicd down -v --remove-orphans || true
237241
@echo "Verifying expected containers are stopped/removed:"
238242
@COMPOSE_PROJECT_NAME=$(COMPOSE_PROJECT_NAME) NIFI_VERSION=$(NIFI_VERSION) docker compose -f $(COMPOSE_FILE) ps --format "table {{.Name}}\t{{.State}}" | tail -n +2 | awk '{print " - " $$1 ": " $$2}' || true
239243

240-
wait-ready: ## wait for readiness using profile configuration; requires NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc
244+
wait-ready: ## wait for readiness using profile configuration; requires NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd
241245
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "ERROR: NIPYAPI_PROFILE is required"; exit 1; fi
242246
@echo "Waiting for $(NIPYAPI_PROFILE) infrastructure to be ready..."
243247
@NIPYAPI_PROFILE=$(NIPYAPI_PROFILE) $(PYTHON) resources/scripts/wait_ready.py
@@ -269,7 +273,7 @@ gen-clients: ## generate NiFi and Registry clients from specs (use wv_spec_varia
269273
# Individual testing
270274

271275
test: ## run pytest with provided NIPYAPI_PROFILE; config resolved by tests/conftest.py
272-
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc)"; exit 1; fi; \
276+
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd)"; exit 1; fi; \
273277
NIPYAPI_PROFILE=$(NIPYAPI_PROFILE) PYTHONPATH=$(PWD):$$PYTHONPATH pytest -q
274278

275279
test-su: ## shortcut: NIPYAPI_PROFILE=single-user pytest
@@ -285,7 +289,7 @@ test-oidc: check-certs ## shortcut: NIPYAPI_PROFILE=secure-oidc pytest (requires
285289
NIPYAPI_PROFILE=secure-oidc $(MAKE) test
286290

287291
test-specific: ## run specific pytest with provided NIPYAPI_PROFILE and TEST_ARGS
288-
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc)"; exit 1; fi; \
292+
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd)"; exit 1; fi; \
289293
if [ -z "$(TEST_ARGS)" ]; then echo "TEST_ARGS is required (e.g., tests/test_utils.py::test_dump -v)"; exit 1; fi; \
290294
NIPYAPI_PROFILE=$(NIPYAPI_PROFILE) PYTHONPATH=$(PWD):$$PYTHONPATH pytest -q $(TEST_ARGS)
291295

@@ -333,8 +337,8 @@ test-all: ensure-certs ## run full e2e tests across automated profiles (requires
333337
done
334338
@echo "All profiles tested successfully"
335339

336-
sandbox: ensure-certs ## create isolated environment with sample objects: make sandbox NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc
337-
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "ERROR: NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc)"; exit 1; fi
340+
sandbox: ensure-certs ## create isolated environment with sample objects: make sandbox NIPYAPI_PROFILE=single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd
341+
@if [ -z "$(NIPYAPI_PROFILE)" ]; then echo "ERROR: NIPYAPI_PROFILE is required (single-user|secure-ldap|secure-mtls|secure-oidc|github-cicd)"; exit 1; fi
338342
@echo "🏗️ Setting up NiPyAPI sandbox with profile: $(NIPYAPI_PROFILE)"
339343
@echo "=== 1/4: Starting infrastructure ==="
340344
$(MAKE) up NIPYAPI_PROFILE=$(NIPYAPI_PROFILE)

docs/history.rst

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,54 @@
22
History
33
=======
44

5+
1.1.0 (2025-12-04)
6+
-------------------
7+
8+
| GitHub CI/CD Integration - Native support for NiFi's GitHub Flow Registry Client
9+
10+
**GitHub Flow Registry Support**
11+
12+
- **Git-specific versioning helpers**: New functions to work with NiFi's native GitHub Flow Registry Client
13+
- ``list_git_registry_buckets``: List buckets (folders) in a Git-backed registry
14+
- ``get_git_registry_bucket``: Get a specific bucket by name
15+
- ``list_git_registry_flows``: List flows in a bucket
16+
- ``get_git_registry_flow``: Get a specific flow by name
17+
- ``list_git_registry_flow_versions``: List all versions (commits) of a flow
18+
- ``deploy_git_registry_flow``: Deploy a versioned flow from GitHub to the NiFi canvas
19+
- ``update_git_flow_ver``: Change version of an already-deployed Git-registry flow
20+
- **Registry client management**: ``ensure_registry_client`` and ``update_registry_client`` for idempotent registry configuration
21+
- **Enhanced revert**: ``revert_flow_ver`` now accepts ``wait=True`` parameter for synchronous operation
22+
23+
**Profile System Enhancements**
24+
25+
- **"env" profile for CI/CD**: New special profile name that configures nipyapi entirely from environment variables
26+
- No profiles file required - ideal for GitHub Actions, containers, and CI/CD pipelines
27+
- Uses ``nipyapi.profiles.switch('env')`` to activate
28+
- All standard environment variable mappings (``NIFI_API_ENDPOINT``, ``NIFI_USERNAME``, etc.) apply
29+
30+
**Controller Service Management**
31+
32+
- **Bulk controller service operations**: ``schedule_all_controllers(pg_id, scheduled)`` to enable/disable all controller services in a process group
33+
- Uses NiFi's native bulk activation API
34+
- Handles all descendant controller services automatically
35+
- Simplifies flow start/stop operations in CI/CD workflows
36+
37+
**Bug Fixes**
38+
39+
- Fixed ``test_create_controller`` leaving orphaned ADLS controller services after test runs
40+
- Improved test cleanup fixtures for better isolation
41+
- Fixed version sorting in ``deploy_git_registry_flow`` to correctly identify latest version
42+
43+
**Infrastructure**
44+
45+
- Updated ``pylintrc`` for pylint 3.x compatibility
46+
- Increased module line limit for versioning.py to accommodate new functions
47+
- Added comprehensive test fixtures for Git registry integration testing
48+
49+
**Related Projects**
50+
51+
- **nipyapi-actions**: Companion GitHub Action for NiFi CI/CD workflows (https://github.com/Chaffelson/nipyapi-actions)
52+
553
1.0.1 (2025-11-10)
654
-------------------
755

@@ -195,41 +243,6 @@ History
195243
* If Client is not instantiated, optimistically instantiate for version checking
196244
* add socks proxy support
197245

198-
0.16.3 (2021-10-11)
199-
-------------------
200-
201-
| Removed force reset of configuration.password and configuration.username to empty string. This was not increasing security, and was causing unexpected errors for users connecting to multiple services in a single script.
202-
| Add greedy control to versioning.get_registry_bucket and versioning.get_flow_in_bucket to avoid undesirable partial string match.
203-
204-
* Update readme to reflect switch from 'master' branch naming to 'main'.
205-
* Update tox to pin testing to Python 3.8, as Python 3.9 is producing unexpected and unrelated SSL failures
206-
* Minor lint formatting improvements
207-
208-
0.16.2 (2021-02-10)
209-
-------------------
210-
211-
| NOTE: If you are using secured Registry, this release will enforce access controls for the swagger interface which is used to determine which version of Registry is connected in order to correctly provide features - you may have to update your authorizations
212-
213-
* Update requirements.txt to unpin future and lxml
214-
* Update lxml to 4.6.2 or newer to resolve vulnerability
215-
* Pin watchdog to <1.0.0 per their docs to maintain Python2.7 compatibility
216-
* Revert 0.14.3 changes to Authentication handling which introduced basicAuth support but resulted in some NiFi connections appearing incorrectly as Anonymous
217-
* Added simpler basicAuth control to force it via a config switch without changing tokenAuth and other Authorization header behavior during normal usage
218-
* nipyapi.config.global_force_basic_auth is now available for use for this purpose
219-
* Secured Registry users will now require the authorization policy to retrieve the swagger so we may use it to validate which version of
220-
* Registry is in use for feature enablement
221-
* Moved all Security controls in config.py to a common area at the foot of the file
222-
* Removed auth_type from security.service_login as it is now redundant
223-
* Added controls to handle certificate checking behavior which has become more strict in recently versions of Python3, ssl_verify and check_hostname are now handled
224-
* security.set_service_auth_token now has an explicit flag for ssl host checking as well
225-
* Fix oversight where improved model serialisation logic was not correctly applied to Registry
226-
* Removed unusused parameter refresh from parameters.update_parameter_context
227-
* Reduced unecessary complexity in utils.dump with no change in functionality
228-
* Updated client gen mustache templates to reflect refactored security and api client code
229-
* Minor linting and docstring and codestyle improvements
230-
* Set pyUp to ignore Watchdog as it must stay between versions to statisfy py2 and py3 compatibility
231-
* If Client is not instantiated, optimistically instantiate for version checking
232-
* add socks proxy support
233246

234247
0.15.0 (2020-11-06)
235248
-------------------

docs/profiles.rst

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ Quick Start
3737
- ``secure-ldap`` - LDAP authentication over TLS
3838
- ``secure-mtls`` - Mutual TLS certificate authentication
3939
- ``secure-oidc`` - OpenID Connect (OAuth2) authentication
40+
- ``bearer-token`` - Pre-obtained JWT bearer token
41+
- ``env`` - Pure environment variable configuration (no profiles file required)
4042

4143
Why Use Profiles?
4244
=================
@@ -87,6 +89,7 @@ Default profiles are defined in ``examples/profiles.yml`` (JSON is also supporte
8789
# Authentication credentials
8890
nifi_user: "einstein"
8991
nifi_pass: "password1234"
92+
nifi_bearer_token: null
9093
registry_user: "einstein"
9194
registry_pass: "password1234"
9295
@@ -132,6 +135,7 @@ Core connection settings:
132135

133136
Authentication credentials:
134137
- ``nifi_user`` / ``nifi_pass`` - NiFi Basic authentication credentials
138+
- ``nifi_bearer_token`` - Pre-obtained JWT bearer token for NiFi
135139
- ``registry_user`` / ``registry_pass`` - Registry Basic authentication credentials
136140

137141
Shared SSL/TLS certificates (simple PKI - convenience options where both NiFi and Registry share configuration):
@@ -154,7 +158,7 @@ Advanced settings:
154158
- ``nifi_proxy_identity`` - Identity for NiFi → Registry proxied requests
155159

156160
Authentication method control:
157-
- ``nifi_auth_method`` - Explicit authentication method for NiFi (overrides auto-detection). Valid values: ``oidc``, ``mtls``, ``basic``
161+
- ``nifi_auth_method`` - Explicit authentication method for NiFi (overrides auto-detection). Valid values: ``bearer``, ``oidc``, ``mtls``, ``basic``
158162
- ``registry_auth_method`` - Explicit authentication method for Registry (overrides auto-detection). Valid values: ``mtls``, ``basic``, ``unauthenticated``
159163

160164
OIDC authentication:
@@ -169,7 +173,7 @@ Profile Switching Behavior
169173
1. **Explicit method specification** (highest priority): If ``nifi_auth_method`` or ``registry_auth_method`` are set, that method is used regardless of other available credentials.
170174
2. **Auto-detection** (fallback): When no explicit method is specified, the system auto-detects based on available credentials. Detection order varies by service:
171175

172-
- **NiFi**: **1) OIDC** (``oidc_token_endpoint``), **2) mTLS** (``client_cert`` + ``client_key``), **3) Basic Auth** (``nifi_user`` + ``nifi_pass``)
176+
- **NiFi**: **1) Bearer Token** (``nifi_bearer_token``), **2) OIDC** (``oidc_token_endpoint``), **3) mTLS** (``client_cert`` + ``client_key``), **4) Basic Auth** (``nifi_user`` + ``nifi_pass``)
173177
- **Registry**: **1) mTLS** (``client_cert`` + ``client_key``), **2) Basic Auth** (``registry_user`` + ``registry_pass``), **3) Unauthenticated** (no credentials required)
174178

175179
For predictable behavior, either use explicit method specification or design profiles with only one authentication method per environment.
@@ -317,6 +321,37 @@ OpenID Connect (OAuth2) authentication:
317321

318322
**Use case**: Modern OAuth2 integration, external identity providers
319323

324+
bearer-token (Simplest Configuration)
325+
--------------------------------------
326+
327+
Pre-obtained JWT bearer token authentication - the simplest possible configuration:
328+
329+
.. code-block:: python
330+
331+
nipyapi.profiles.switch('bearer-token')
332+
333+
**Authentication method**: Bearer Token (detected by presence of ``nifi_bearer_token``)
334+
335+
**Required properties**:
336+
- ``nifi_bearer_token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."`` (your JWT token)
337+
338+
**Additional properties used**:
339+
- ``nifi_url: https://nifi.example.com/nifi-api``
340+
- ``nifi_verify_ssl: true`` (or false for self-signed certificates)
341+
342+
**Use case**: CI/CD pipelines, GitHub Actions, Kubernetes jobs, or any scenario where you have a pre-obtained JWT token from your identity provider. This is the simplest authentication method - just a URL and a token.
343+
344+
**Example using environment variables only**:
345+
346+
.. code-block:: shell
347+
348+
# Set environment variables
349+
export NIFI_API_ENDPOINT=https://nifi.production.example.com/nifi-api
350+
export NIFI_BEARER_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
351+
352+
# Use the 'env' profile to load from environment
353+
python -c "import nipyapi; nipyapi.profiles.switch('env')"
354+
320355
cli-properties
321356
--------------
322357

@@ -342,6 +377,34 @@ NiFi CLI properties file integration with OIDC Client Credentials flow:
342377

343378
**Use case**: Integration with existing NiFi CLI configurations, client credentials OIDC
344379

380+
env (Environment-Only Configuration)
381+
------------------------------------
382+
383+
Pure environment variable configuration without requiring a profiles file:
384+
385+
.. code-block:: python
386+
387+
nipyapi.profiles.switch('env')
388+
389+
**Authentication method**: Auto-detected from environment variables
390+
391+
**Required environment variables** (minimum for NiFi connection):
392+
- ``NIFI_API_ENDPOINT`` - NiFi API URL
393+
- ``NIFI_USERNAME`` - NiFi username (for basic auth)
394+
- ``NIFI_PASSWORD`` - NiFi password (for basic auth)
395+
396+
**Optional environment variables**:
397+
- ``REGISTRY_API_ENDPOINT`` - Registry API URL
398+
- ``REGISTRY_USERNAME`` / ``REGISTRY_PASSWORD`` - Registry credentials
399+
- ``TLS_CA_CERT_PATH`` - CA certificate path
400+
- ``MTLS_CLIENT_CERT`` / ``MTLS_CLIENT_KEY`` - mTLS certificates
401+
- ``OIDC_TOKEN_ENDPOINT`` / ``OIDC_CLIENT_ID`` / ``OIDC_CLIENT_SECRET`` - OIDC configuration
402+
- All other environment variables listed in the Environment Variable Overrides section
403+
404+
**Use case**: CI/CD pipelines, containerized deployments, GitHub Actions, any environment where configuration is injected via environment variables rather than files.
405+
406+
**Key benefit**: No profiles file needed. The ``env`` profile starts with all-null defaults and populates values entirely from environment variables using the standard ``ENV_VAR_MAPPINGS``. This keeps secrets out of files and allows dynamic configuration in deployment environments.
407+
345408
Environment Variable Overrides
346409
===============================
347410

@@ -381,6 +444,7 @@ URLs and credentials:
381444
- ``REGISTRY_API_ENDPOINT`` → ``registry_url``
382445
- ``NIFI_USERNAME`` → ``nifi_user``
383446
- ``NIFI_PASSWORD`` → ``nifi_pass``
447+
- ``NIFI_BEARER_TOKEN`` → ``nifi_bearer_token``
384448
- ``REGISTRY_USERNAME`` → ``registry_user``
385449
- ``REGISTRY_PASSWORD`` → ``registry_pass``
386450

@@ -504,6 +568,12 @@ You don't have to specify values that are otherwise null unless required for tha
504568

505569
You don't have to use profiles. You can also directly configure the NiPyAPI client using ``nipyapi.config`` and ``nipyapi.utils.set_endpoint()`` as shown in earlier examples.
506570

571+
GitHub Flow Registry Client
572+
===========================
573+
574+
NiFi 2.x supports versioning flows directly to GitHub repositories using the GitHub Flow Registry Client.
575+
For CI/CD workflows with GitHub Actions, see the `nipyapi-actions <https://github.com/Chaffelson/nipyapi-actions>`_ repository which provides reusable actions for deploying NiFi flows from Git repositories.
576+
507577
Integration with Examples
508578
=========================
509579

examples/profiles.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,26 @@ secure-oidc:
8585
oidc_client_id: "nipyapi-client"
8686
oidc_client_secret: "nipyapi-secret"
8787

88+
# Bearer token profile - simplest possible configuration
89+
# Use when you have a pre-obtained JWT token (e.g., from CI/CD, identity provider)
90+
# This is ideal for GitHub Actions, Kubernetes jobs, or any environment with injected tokens
91+
bearer-token:
92+
nifi_url: https://nifi.example.com/nifi-api
93+
nifi_bearer_token: null # Set via NIFI_BEARER_TOKEN environment variable
94+
nifi_verify_ssl: true
95+
96+
# GitHub CI/CD profile - NiFi only, for testing Git-based registry workflows
97+
# Uses GitHub Registry Client instead of NiFi Registry
98+
# GitHub registry configuration is handled by nipyapi-actions, not nipyapi profiles
99+
github-cicd:
100+
nifi_url: https://localhost:9447/nifi-api
101+
nifi_user: einstein
102+
nifi_pass: password1234
103+
nifi_verify_ssl: false
104+
suppress_ssl_warnings: true
105+
# No NiFi Registry - using GitHub as flow registry
106+
registry_url: null
107+
88108
# Example profile using NiFi CLI properties file integration
89109
cli-properties:
90110
# Load configuration from existing NiFi CLI properties file

0 commit comments

Comments
 (0)