| name | openflow-gdrive-cleanup |
| description | Clean up the Google Drive CDC connector demo. Stops flow, drops schema, resets gate state. |
| parent_skill | openflow-gdrive-demo |
OpenFlow Google Drive — Cleanup
Tear down the connector deployment and reset for a fresh demo run.
When to Load
Parent SKILL.md routes here on: "cleanup", "clean", "reset", "tear down", "remove"
Forbidden Actions
- NEVER drop the RUNTIMES schema — it contains OpenFlow runtime definitions and is shared infrastructure
- NEVER drop the NETWORKS schema -- it contains network rules tracked in
.sfutils/manifest.toml
- NEVER drop the POLICIES schema -- it contains auth policies tracked in
.sfutils/manifest.toml
- NEVER drop the database itself — only drop the demo-specific destination schema
- NEVER delete
.sfutils/ — manifest is preserved across runs
Correct Cleanup Order
The cleanup MUST follow this dependency order:
- Stop flow (processors)
- Disable controllers
- Delete parameter contexts (BEFORE deleting PG — contexts are orphaned if PG deleted first)
- Delete process group
- Detach EAI from runtime (if attached)
- Drop destination schema (only the demo schema, NEVER infrastructure schemas)
- Remove PAT / service user (if full cleanup requested)
- Remove nipyapi profile
- Reset gate state + manifest
Workflow
Step 1: Resolve Config
eval $(python3 -c "
import tomllib, os
m = tomllib.load(open('.sfutils/manifest.toml', 'rb'))
s = m.get('snowflake', {})
o = m.get('openflow', {})
rt = o.get('runtime', {})
wf = o.get('workflows', {})
print(f'CONNECTION={s.get(\"connection\", \"\")}')
print(f'DATABASE={o.get(\"database\", \"\")}')
print(f'SCHEMA={o.get(\"schema\", \"\")}')
print(f'ROLE={o.get(\"role\", \"\")}')
print(f'PROFILE={rt.get(\"nipyapi_profile\", \"\")}')
print(f'EAI={o.get(\"eai\", \"\")}')
print(f'RUNTIME_FQN={rt.get(\"name\", \"\")}')
# Get PG_ID from first workflow if available
for k, v in wf.items():
if isinstance(v, dict) and v.get('pg_id'):
print(f'PG_ID={v[\"pg_id\"]}')
break
")
Step 2: Find and Stop the Flow
nipyapi --profile $PROFILE ci list_flows
Find the connector PG and get its ID. Stop the flow:
nipyapi --profile $PROFILE canvas schedule_process_group "<PG_ID>" False
Step 3: Disable Controllers
nipyapi --profile $PROFILE canvas schedule_all_controllers "<PG_ID>" False
Step 4: Delete Parameter Contexts
IMPORTANT: This MUST happen BEFORE deleting the process group. If the PG is deleted first, parameter contexts become orphaned and will conflict on the next deployment.
Delete in reverse-dependency order: The Ingestion context references the Destination context. Delete Ingestion first, then Destination, then any others.
Run via uv run to access the nipyapi Python dependency:
uv run --project <SKILL_DIR> python -c "
import nipyapi
nipyapi.profiles.switch('$PROFILE')
contexts = nipyapi.nifi.FlowApi().get_parameter_contexts()
# Sort: delete Ingestion before Destination (reverse dependency order)
ORDER = ['Ingestion', 'Destination', 'Connection']
matching = [ctx for ctx in contexts.parameter_contexts if 'Google Drive' in ctx.component.name]
matching.sort(key=lambda c: next((i for i, k in enumerate(ORDER) if k in c.component.name), 99), reverse=False)
for ctx in matching:
print(f'Deleting parameter context: {ctx.component.name}')
nipyapi.parameters.delete_parameter_context(ctx.id)
print(f' Deleted')
"
Where <SKILL_DIR> is the absolute path to skills/openflow-gdrive-demo/ (contains pyproject.toml with nipyapi dependency).
Step 5: Delete Process Group
nipyapi --profile $PROFILE canvas delete_process_group "<PG_ID>" True
The True flag forces deletion (purges queued flowfiles).
Step 6: Detach EAI from Runtime
If an EAI was attached to the runtime for this connector, ask the user to detach it via Snowsight:
Please detach EAI {EAI_NAME} from your runtime:
- Go to Snowsight > Ingestion > OpenFlow > Deployments > your runtime
- Edit the runtime and remove
{EAI_NAME} from External Access Integrations
- Save
STOP: Wait for user to confirm.
Step 7: Drop Destination Schema
⚠️ SAFETY CHECK: Only drop the demo-specific schema. NEVER drop infrastructure schemas.
Protected schemas (NEVER drop): RUNTIMES, NETWORKS, POLICIES, INFORMATION_SCHEMA, PUBLIC
snow sql -q "DROP SCHEMA IF EXISTS $DATABASE.$SCHEMA CASCADE" -c $CONNECTION --role ACCOUNTADMIN
Removes all tables, stages, Cortex Search service, dynamic tables, and procedures in the demo schema.
Step 8: Remove PAT and Service User (if full cleanup)
If user requested full cleanup (including PAT/service-user):
<PAT_SKILL_DIR>/pat remove --user $SA_USER --db $SFUTILS_DB --drop-user --yes
snow sql -q "DROP ROLE IF EXISTS $SA_ROLE" -c $CONNECTION --role ACCOUNTADMIN
Then remove the nipyapi profile:
import yaml
profiles_path = '~/.nipyapi/profiles.yml'
Step 9: Reset Gate State and Manifest
python3 .cortex/skills/openflow-gdrive-demo/scripts/gate.py \
--step deploy --action reset --cascade --manifest .sfutils/manifest.toml
Remove [openflow], [openflow.*], and [pat.*] (if REMOVED) sections from manifest, leaving only [snowflake] and [prereqs].
Step 10: Verify Cleanup
-
Confirm no connector flow remains:
nipyapi --profile $PROFILE ci list_flows
-
Confirm no orphaned parameter contexts:
contexts = nipyapi.nifi.FlowApi().get_parameter_contexts()
assert len(contexts.parameter_contexts) == 0
-
Confirm schema is gone:
snow sql -q "SHOW SCHEMAS IN DATABASE $DATABASE" -c $CONNECTION
-
Confirm EAI detached (user confirmed in Step 6).
-
Confirm service user removed (if full cleanup):
snow sql -q "SHOW USERS LIKE '%$SA_USER%'" -c $CONNECTION --role ACCOUNTADMIN
Stopping Points
- ✋ Before Step 7: confirm which schema to drop (safety check)
- ✋ Before Step 8: confirm full cleanup if PAT/user removal requested
- ✋ After Step 10: present cleanup verification
Output
Demo cleaned up!
Flow: Deleted
Parameter Ctxs: Deleted
EAI: Detached from runtime (integration retained)
Schema: Dropped (CASCADE)
PAT/User: [Removed | Retained]
Gate: All steps reset
Manifest: Reset to base state
Ready for fresh demo deployment