agentleFS
NVIDIAKnown publisherNVIDIA/ai-cloud-validation/.cursor/skills/storage-api-stub-authoring/SKILL.md

storage-api-stub-authoring

Skill35 starsChanged 7 months agoApache-2.0View on GitHub

Guides implementation of Storage Provider shims in-place under isvctl/configs/providers/my-isv/ — fill TODO blocks in scripts/storage/api.py and edit storage-provider-manifest.yaml. Does not copy to new provider dirs or scaffold full provider trees. Walks through each method with pointed questions and runs isolated sanity probes. Use when the user mentions storage shim, StorageApi, storage-provider-manifest, StorageProviderApiCheck, or storage provider stubs.

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 6 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈6

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Ask permission before any discovery command: If granted, run probes from repo root.

    line 102
  2. Very specific:

    Network locality: If the management API is only reachable in-cluster (VAST VMS pattern), note that isvctl deploy run may be required …

    line 140
  3. Very specific:

    Do not: create providers/<customer>/ or copy these files elsewhere unless prompted by the user.

    line 155
  4. Very specific:

    Read scripts/storage/api.py end-to-end — every method has a TODO block to replace

    line 159
  5. Distinctive:

    Read AWS/VAST reference shims for patterns matching intake answers

    line 160
  6. Very specific:

    Update config/overlays/storage-provider-manifest.yaml (v1alpha2): providers[].name, identity/provider, shim, capabilities — see …

    line 161
  7. Very specific:

    Keep shim.module: ../../scripts/storage/api.py (relative to config/overlays/)

    line 162
  8. Very specific:

    Keep DEMO_MODE until real backend calls land — enables smoke without credentials

    line 163
  9. Distinctive:

    Follow method-walkthrough.md.

    line 219
  10. Distinctive:

    Run before full suite. Order matters.

    line 248
  11. Distinctive:

    Use patterns from the reference READMEs:

    line 272
  12. Distinctive:

    Gate: Do not proceed to Phase 5 until 4a–4c pass (4d acceptable during scaffolding).

    line 302
  13. Very specific:

    Run only when the user wants end-to-end StorageProviderApiCheck validation.

    line 308
  14. Very specific:

    Only after Phase 5 passes.

    line 337

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Storage API Stub Authoring

Interactive workflow for implementing a provider's storage shim. Follow this skill end-to-end; do not skip intake or sanity gates.

Scope boundaries (read first)

Default workspace: isvctl/configs/providers/my-isv/

Implement by filling in the existing stubs — replace TODO blocks in scripts/storage/api.py and update the sibling manifest. Do not cp or cp -r my-isv to providers/<customer>/ unless the user explicitly requests a separate handoff folder.

In scope (my-isv)Path
Requiredscripts/storage/api.py — edit MyStorageApi TODO blocks in place
Requiredconfig/overlays/storage-provider-manifest.yaml
Harness (exists)config/storage.yaml — tweak in place if needed for tests
Optionalscripts/storage/README.md (document env vars when stub is done)

AWS/VAST/weka paths under providers/aws/, providers/vast/, etc. are read-only references — study them; do not copy their layout into new dirs.

Out of scope — do not create, copy, or edit unless the user explicitly asks:

  • New provider directories (providers/<customer>/)
  • Other config/*.yaml or scripts/** trees in my-isv (if any exist)
  • Full-provider scaffold (cp -r my-isv …)

Running my-isv/config/storage.yaml with --capability kubernetes is optional, only when the user wants k8s-integrated runs.

Outcomes

By session end the user should have:

  1. config/overlays/storage-provider-manifest.yaml declaring the provider and pointing at the shim
  2. scripts/storage/.../api.py implementing StorageApi + build_api()
  3. Documented env vars in scripts/storage/README.md (when stub is complete)
  4. Passing isolated probes (probe_shim.py) before any full isvctl test run
  5. config/storage.yaml's storage_manifest step still pointing at the manifest (already declared)

Reference implementations (read before coding)

BackendShimManifestConfig
AWS FSx Lustreisvctl/configs/providers/aws/scripts/storage/fsx-lustre/api.pyaws/config/overlays/storage-provider-manifest.yamlaws/config/storage.yaml
VAST NFSisvctl/configs/providers/vast/scripts/storage/vast/api.pyvast/config/overlays/storage-provider-manifest.yamlvast/config/storage.yaml
WEKAisvctl/configs/providers/weka/scripts/storage/weka/api.pyweka/config/overlays/storage-provider-manifest.yamlweka/config/storage.yaml
Authoring target (edit in place)my-isv/scripts/storage/api.pymy-isv/config/overlays/storage-provider-manifest.yamlmy-isv/config/storage.yaml

Contract: isvtest/src/isvtest/core/storage_provider/api.py (per-method docstrings cover inputs/outputs, error conditions, and capability/qualifier semantics) Check drivers:

  • isvtest/src/isvtest/validations/storage_provider.py (StorageProviderApiCheck)
  • isvtest/src/isvtest/validations/storage_quota_enforcement.py (StorageDirectoryQuotaEnforcementCheck — live CRUD + write enforcement; needs K8s)

Manifest (schema v1alpha2):

  • Schema: isvctl/schemas/storage-provider-manifest.schema.json
  • Fully-populated example: my-isv/config/overlays/storage-provider-manifest.example.yaml
  • Manifest → step adapter: isvctl/configs/shared/storage/storage_manifest_to_steps.py (resolves the manifest path, loads no shim; the storage_manifest step in storage.yaml)

Session workflow

Copy this checklist and update as you go:

Progress:
- [ ] Phase 0: Intake + environment permission (discover what's there)
- [ ] Phase 1: Confirm my-isv files
- [ ] Phase 2: Generate manifest (discover + Q&A) + config wiring
- [ ] Phase 3: Implement StorageApi methods (one at a time)
- [ ] Phase 4: Isolated sanity probes
- [ ] Phase 5: Targeted StorageProviderApiCheck run
- [ ] Phase 6: Full suite guidance (optional)

Phase 0: Intake and environment access

Ask permission before any discovery command. If granted, run probes from repo root. If denied, ask the user to paste command output.

Work through intake-questions.md first. Summarize answers in a short "Implementation profile" before writing code.

Environment discovery (when permitted)

Discovery is the first half of manifest generation: probe the cluster/API to propose field values, then confirm the rest by Q&A. The full discover-vs-ask mapping for every manifest field is in manifest-generation.md.

Kubernetes (adjust namespace/context as needed):

kubectl config current-context
kubectl get storageclass -o wide
kubectl get sc <sc-name> -o yaml    # provisioner, parameters, mountOptions
kubectl get csidriver
kubectl get nodes --show-labels    # -> node_selector for the CSI/filesystem checks
kubectl get pods -A | grep -i csi  # driver health hint + which nodes run node plugin

Map findings into the v1alpha2 manifest (which drives StorageProviderApiCheck ONLY):

  • providers[].provider.* / identity.* (infer vendor/protocol/type from the driver; ask for version)
  • providers[].capabilities.* (what the shim's properties() reports)
  • providers[].shim.* (module path / configmap / credentials_secret)

The StorageClass names, NFS expectations, and node selectors are NOT in the manifest: the CSI / filesystem checks (K8sCsiStorageTypesCheck, K8sCsiDriverHealthCheck, K8sFile*, K8sNfsMountOptionsCheck, …) read them from the suite/provider config or the K8S_CSI_* env vars (see config-wiring.md).

Network locality: If the management API is only reachable in-cluster (VAST VMS pattern), note that isvctl deploy run may be required instead of laptop-side isvctl test run.


Phase 1: Work in my-isv

Open the existing files — they already contain TODO markers and DEMO_MODE:

isvctl/configs/providers/my-isv/
├── config/overlays/storage-provider-manifest.yaml   # shim.module → ../../scripts/storage/api.py
├── config/storage.yaml                     # storage_manifest step already points at the manifest
└── scripts/storage/api.py                  # MyStorageApi — fill each TODO block

Do not create providers/<customer>/ or copy these files elsewhere unless prompted by the user.

Before coding:

  1. Read scripts/storage/api.py end-to-end — every method has a TODO block to replace
  2. Read AWS/VAST reference shims for patterns matching intake answers
  3. Update config/overlays/storage-provider-manifest.yaml (v1alpha2): providers[].name, identity/provider, shim, capabilities — see manifest-generation.md
  4. Keep shim.module: ../../scripts/storage/api.py (relative to config/overlays/)
  5. Keep DEMO_MODE until real backend calls land — enables smoke without credentials

Rename MyStorageApi / properties() identity fields to match the backend; keep build_api() at module level.


Phase 2: Manifest generation and config wiring

Generate config/overlays/storage-provider-manifest.yaml (schema v1alpha2) using the discover-then-ask workflow in manifest-generation.md: probe the environment to propose field values, ask the customer for what you can't observe, and omit (don't guess) anything still unknown so the dependent check skips cleanly. config/storage.yaml already declares the storage_manifest step that feeds it to the checks — see config-wiring.md.

Two files ship beside the template — use them as you fill the blank one:

  • config/overlays/storage-provider-manifest.yaml — blank template to edit in place
  • config/overlays/storage-provider-manifest.example.yaml — fully-populated v1alpha2 reference (every field)

The manifest is a contract, not docs. For providers with a shim: block, StorageProviderApiCheck cross-checks declared identity/capabilities against the running shim and fails on mismatch (manifest-consistency[<name>]). Only declare what the shim actually reports; omit a field to leave it unchecked.

Validate the draft without a backend (parses the manifest, loads no shim):

STORAGE_PROVIDER_MANIFEST=isvctl/configs/providers/my-isv/config/overlays/storage-provider-manifest.yaml \
  uv run python isvctl/configs/shared/storage/storage_manifest_to_steps.py | uv run python -m json.tool

Critical couplings:

ArtifactPurpose
shim.moduleRelative to manifest parent; must resolve to api.py with build_api()
storage_manifest step in provider YAMLSTORAGE_PROVIDER_MANIFEST resolves to the manifest_path passed to StorageProviderApiCheck (on-disk now; ConfigMap mount path in prod)
Env vars in shim __init__Runtime config — manifest attributes are informational only
K8S_CSI_* env vars / config overridesStorageClass names for the CSI/filesystem checks — set in config/env, NOT the manifest

Provider wiring pattern: the suite's storage_provider_api group is bound to the storage_manifest step — see config-wiring.md.

ConfigMap handoff (document for customer, do not implement operator):

  • Manifest → ConfigMap storage-provider-manifest, key manifest.yaml
  • Shim api.py → per-provider ConfigMap referenced by shim.configmap
  • Credentials → per-provider Secret; env vars at runtime

Phase 3: Implement methods (one at a time)

Follow method-walkthrough.md. Implement and probe each method before moving on. Ask the pointed questions listed there.

Decision tree: volume lifecycle

Does a CSI driver dynamically provision PVCs on this cluster?
├─ YES (managed K8s — AWS FSx, VAST, WEKA pattern)
│    → create_volume / delete_volume raise NotSupportedError
│    → list_volumes MUST return existing CSI-provisioned volumes (count ≥ 1 for N-020 pass)
│    → Populate Volume.csi (driver + volume_handle) when discoverable
└─ NO (bare-metal / API-managed storage)
     → Implement create_volume + delete_volume
     → Tag volumes with isvtest-run-id for orphan sweep

ProviderProperties

Declare capabilities honestly in storage-provider-manifest.yaml, and implement only the corresponding methods in scripts/storage/api.py. new_implementation() detects which surfaces are backed and advertises the merged capability set. Directory-quota shims should also exercise StorageDirectoryQuotaEnforcementCheck under a K8s config (optional pvc_name / pod_name reuse for faster iteration — see provider READMEs).


Phase 4: Isolated sanity probes

Run before full suite. Order matters.

4a. Manifest loads

uv run python -c "
from isvtest.core.storage import load_provider_registry
ps = load_provider_registry({'manifest_path': 'isvctl/configs/providers/my-isv/config/overlays/storage-provider-manifest.yaml'})
print([p.name for p in ps if p.has_shim])
"

4b. Shim probe script

uv run python .cursor/skills/storage-api-stub-authoring/scripts/probe_shim.py \
  --manifest isvctl/configs/providers/my-isv/config/overlays/storage-provider-manifest.yaml

Expect: health_check ok, get_tenant_quota with hard_limit_bytes > 0, and either create_volume path or list_volumes returning ≥1 volume (CSI fallback).

4c. Backend-specific CLI probes

Use patterns from the reference READMEs:

  • AWS: aws sts get-caller-identity, aws fsx describe-file-systems, aws service-quotas get-service-quota
  • VAST: curl -H "Authorization: Api-Token $VAST_TOKEN" "https://$VAST_ENDPOINT/api/quotas/"
  • Generic: whatever authenticated GET the shim's health_check uses

4d. Demo mode smoke (no credentials, optional)

Uses the existing my-isv/config/storage.yaml harness:

ISVCTL_DEMO_MODE=1 \
  uv run isvctl test run -f isvctl/configs/providers/my-isv/config/storage.yaml

4e. Contract / validation unit tests

# Shim contract package (ABC, loader, mock)
uv run pytest isvtest/src/isvtest/core/storage_provider/tests/

# StorageProviderApiCheck behavior (framework tests)
uv run pytest isvtest/tests/test_storage_provider.py

# Provider-specific hermetic shim tests, when added:
uv run pytest isvtest/tests/test_aws_fsx_lustre_shim.py
uv run pytest isvtest/tests/test_weka_shim.py
uv run pytest isvtest/tests/test_storage_quota_enforcement.py

Gate: Do not proceed to Phase 5 until 4a–4c pass (4d acceptable during scaffolding).


Phase 5: Targeted acceptance check (optional)

Run only when the user wants end-to-end StorageProviderApiCheck validation. Requires a storage.yaml with a storage_manifest step.

uv run isvctl test run -f isvctl/configs/providers/my-isv/config/storage.yaml

For K8s-integrated runs, run the same storage.yaml with --capability kubernetes — do not create a new config or a full provider tree.

Expected CSI fallback (not a failure):

volume-provisioning[<name>] SKIPPED: create_volume not implemented; observed N CSI-provisioned volume(s)

Common failures → fixes:

SymptomFix
AuthenticationError on health_checkFix creds / network / IAM
hard_limit_bytes=0Wrong quota source or empty tenant
observed 0 ... via list_volumesCreate a PVC against the StorageClass first
Manifest not foundRun from repo root; fix manifest_path

Phase 6: Full suite (optional)

Only after Phase 5 passes. Run storage.yaml (which imports only isvctl/configs/suites/storage.yaml, never alongside suites/k8s.yaml) with --capability kubernetes, and align:

  • StorageClass names via the K8S_CSI_* env vars (K8S_CSI_BLOCK_SC, K8S_CSI_SHARED_FS_SC, K8S_CSI_NFS_SC) or literal overrides in your config — these are NOT in the manifest
  • k8s_storage CSI checks beyond the shim: K8sCsiStorageTypesCheck, K8sCsiStorageQuotaApiCheck, K8sCsiProvisioningModesCheck, K8sCsiConcurrentPvcCheck, K8sCsiPvcExpandCheck, K8sCsiDriverHealthCheck
  • k8s_filesystem checks (K8sFileLockingCheck, K8sCrossNodeWriteVisibilityCheck, …) and node_selector (literal dict) when CSI requires labeled nodes
uv run isvctl test run -f isvctl/configs/providers/my-isv/config/<k8s-or-storage-config>.yaml

Agent behavior rules

  1. Edit my-isv in place — fill scripts/storage/api.py TODO blocks; never cp -r my-isv to a new provider dir unless the user explicitly asks for handoff.
  2. One method at a time — implement properties + __init__, then health_check, then get_tenant_quota, then volume methods.
  3. Ask before assuming — especially CSI vs API provisioning, tenant model, quota semantics.
  4. Match reference style — env-driven config, tight IAM/API surface, NotSupportedError for CSI-owned lifecycle.
  5. Do not add setup scripts — this skill does not author provider orchestration steps.
  6. Document env vars in scripts/storage/README.md when the stub is done (in-scope).
  7. Test harness on request — run storage.yaml only when the user wants to run checks; its storage_manifest step is already wired.

Additional resources

  • manifest-generation.md — discover-then-ask, field-by-field manifest authoring (v1alpha2)
  • intake-questions.md — upfront questionnaire
  • method-walkthrough.md — per-method questions and acceptance criteria
  • config-wiring.md — manifest, overrides, CSI, ConfigMap
  • AWS stub implementation
  • VAST stub implementation
  • WEKA stub implementation

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.