Skip to content

Commit af0249f

Browse files
committed
feat: Add GitHub CI/CD versioning support with env profile
Add native support for NiFi's GitHub Flow Registry Client to enable CI/CD workflows for versioned NiFi flows. New 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 - ensure_registry_client, update_registry_client Profile system enhancements: - Add 'env' profile for pure environment variable configuration - Ideal for GitHub Actions, containers, and CI/CD pipelines - No profiles file required when using nipyapi.profiles.switch('env') Controller service management: - Add schedule_all_controllers for bulk enable/disable operations - Uses NiFi's native bulk activation API with descendant support Bug fixes: - Fix test_create_controller leaving orphaned controller services Documentation: - Add 1.1.0 release notes to history - Add env profile usage guide to profiles documentation Related: nipyapi-actions and nipyapi-workflow companion repositories
1 parent 107e324 commit af0249f

File tree

18 files changed

+1908
-687
lines changed

18 files changed

+1908
-687
lines changed

.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 GITHUB_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: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,47 @@
22
History
33
=======
44

5+
1.1.0 (2025-12-XX)
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+
- **Registry client management**: ``ensure_registry_client`` and ``update_registry_client`` for idempotent registry configuration
20+
21+
**Profile System Enhancements**
22+
23+
- **"env" profile for CI/CD**: New special profile name that configures nipyapi entirely from environment variables
24+
- No profiles file required - ideal for GitHub Actions, containers, and CI/CD pipelines
25+
- Uses ``nipyapi.profiles.switch('env')`` to activate
26+
- All standard environment variable mappings (``NIFI_API_ENDPOINT``, ``NIFI_USERNAME``, etc.) apply
27+
- **Documentation**: Added "env" profile usage guide to profiles documentation
28+
29+
**Controller Service Management**
30+
31+
- **Bulk controller service operations**: ``schedule_all_controllers(pg_id, scheduled)`` to enable/disable all controller services in a process group
32+
- Uses NiFi's native bulk activation API
33+
- Handles all descendant controller services automatically
34+
- Simplifies flow start/stop operations in CI/CD workflows
35+
36+
**Bug Fixes**
37+
38+
- Fixed ``test_create_controller`` leaving orphaned ADLS controller services after test runs
39+
- Improved test cleanup fixtures for better isolation
40+
41+
**Related Projects**
42+
43+
- **nipyapi-actions**: Companion GitHub Action for NiFi CI/CD workflows (separate repository)
44+
- **nipyapi-workflow**: Demo repository showing PR-based flow testing patterns
45+
546
1.0.1 (2025-11-10)
647
-------------------
748

@@ -195,41 +236,6 @@ History
195236
* If Client is not instantiated, optimistically instantiate for version checking
196237
* add socks proxy support
197238

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
233239

234240
0.15.0 (2020-11-06)
235241
-------------------

docs/profiles.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Quick Start
3737
- ``secure-ldap`` - LDAP authentication over TLS
3838
- ``secure-mtls`` - Mutual TLS certificate authentication
3939
- ``secure-oidc`` - OpenID Connect (OAuth2) authentication
40+
- ``env`` - Pure environment variable configuration (no profiles file required)
4041

4142
Why Use Profiles?
4243
=================
@@ -342,6 +343,34 @@ NiFi CLI properties file integration with OIDC Client Credentials flow:
342343

343344
**Use case**: Integration with existing NiFi CLI configurations, client credentials OIDC
344345

346+
env (Environment-Only Configuration)
347+
------------------------------------
348+
349+
Pure environment variable configuration without requiring a profiles file:
350+
351+
.. code-block:: python
352+
353+
nipyapi.profiles.switch('env')
354+
355+
**Authentication method**: Auto-detected from environment variables
356+
357+
**Required environment variables** (minimum for NiFi connection):
358+
- ``NIFI_API_ENDPOINT`` - NiFi API URL
359+
- ``NIFI_USERNAME`` - NiFi username (for basic auth)
360+
- ``NIFI_PASSWORD`` - NiFi password (for basic auth)
361+
362+
**Optional environment variables**:
363+
- ``REGISTRY_API_ENDPOINT`` - Registry API URL
364+
- ``REGISTRY_USERNAME`` / ``REGISTRY_PASSWORD`` - Registry credentials
365+
- ``TLS_CA_CERT_PATH`` - CA certificate path
366+
- ``MTLS_CLIENT_CERT`` / ``MTLS_CLIENT_KEY`` - mTLS certificates
367+
- ``OIDC_TOKEN_ENDPOINT`` / ``OIDC_CLIENT_ID`` / ``OIDC_CLIENT_SECRET`` - OIDC configuration
368+
- All other environment variables listed in the Environment Variable Overrides section
369+
370+
**Use case**: CI/CD pipelines, containerized deployments, GitHub Actions, any environment where configuration is injected via environment variables rather than files.
371+
372+
**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.
373+
345374
Environment Variable Overrides
346375
===============================
347376

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

505534
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.
506535

536+
GitHub Flow Registry Client
537+
===========================
538+
539+
NiFi 2.x supports versioning flows directly to GitHub repositories using the GitHub Flow Registry Client.
540+
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.
541+
507542
Integration with Examples
508543
=========================
509544

examples/profiles.yml

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

88+
# GitHub CI/CD profile - NiFi only, for testing Git-based registry workflows
89+
# Uses GitHub Registry Client instead of NiFi Registry
90+
# GitHub registry configuration is handled by nipyapi-actions, not nipyapi profiles
91+
github-cicd:
92+
nifi_url: https://localhost:9447/nifi-api
93+
nifi_user: einstein
94+
nifi_pass: password1234
95+
nifi_verify_ssl: false
96+
suppress_ssl_warnings: true
97+
# No NiFi Registry - using GitHub as flow registry
98+
registry_url: null
99+
88100
# Example profile using NiFi CLI properties file integration
89101
cli-properties:
90102
# Load configuration from existing NiFi CLI properties file

nipyapi/canvas.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"delete_controller",
4747
"update_controller",
4848
"schedule_controller",
49+
"schedule_all_controllers",
4950
"get_controller",
5051
"list_all_controller_types",
5152
"list_all_by_kind",
@@ -1216,6 +1217,33 @@ def _schedule_controller_state(cont_id, tgt_state):
12161217
raise ValueError("Scheduling request timed out")
12171218

12181219

1220+
def schedule_all_controllers(pg_id, scheduled):
1221+
"""
1222+
Enable or Disable all Controller Services in a Process Group.
1223+
1224+
Uses NiFi's native bulk controller service activation API which handles
1225+
all descendant controller services automatically.
1226+
1227+
Args:
1228+
pg_id (str): The UUID of the Process Group
1229+
scheduled (bool): True to enable, False to disable
1230+
1231+
Returns:
1232+
ActivateControllerServicesEntity: The result of the operation
1233+
1234+
"""
1235+
assert isinstance(pg_id, str)
1236+
assert isinstance(scheduled, bool)
1237+
1238+
target_state = "ENABLED" if scheduled else "DISABLED"
1239+
1240+
with nipyapi.utils.rest_exceptions():
1241+
return nipyapi.nifi.FlowApi().activate_controller_services(
1242+
id=pg_id,
1243+
body=nipyapi.nifi.ActivateControllerServicesEntity(id=pg_id, state=target_state),
1244+
)
1245+
1246+
12191247
def get_controller(
12201248
identifier, identifier_type="name", bool_response=False, include_reporting_tasks=True
12211249
):

nipyapi/config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@
6969
"ControllerServiceEntity": {"id": ["id"], "name": ["component", "name"]},
7070
"ParameterContextEntity": {"id": ["id"], "name": ["component", "name"]},
7171
"ReportingTaskEntity": {"id": ["id"], "name": ["component", "name"]},
72+
# Git-based Flow Registry types (GitHub, GitLab, Bitbucket, Azure DevOps)
73+
"FlowRegistryBucketEntity": {"id": ["id"], "name": ["bucket", "name"]},
74+
"VersionedFlowEntity": {
75+
"flow_id": ["versioned_flow", "flow_id"],
76+
"flow_name": ["versioned_flow", "flow_name"],
77+
},
78+
"VersionedFlowDTO": {"flow_id": ["flow_id"], "flow_name": ["flow_name"]},
7279
}
7380

7481

0 commit comments

Comments
 (0)