Skip to content

About

OTC-native Kubernetes CSI driver for dynamic EVS PersistentVolumes (AK/SK auth)

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

csi-driver-otc

Warning

Experimental — for demo purposes only. DO NOT USE IN PRODUCTION. This project is an experiment and is provided as-is, without any guarantees of stability, support, or maintenance.

A Kubernetes Container Storage Interface (CSI) driver for Open Telekom Cloud (OTC), providing dynamic provisioning of persistent volumes backed by OTC's Elastic Volume Service (EVS). This is a fresh, OTC-native driver authenticated via AK/SK credentials, not based on upstream cinder-csi-plugin.

Status: v1 (dynamic provisioning), live-verified against OTC eu-de — full PVC → EVS create → attach → mount → write → detach → delete lifecycle with zero orphaned resources.

Note

This project was developed with substantial AI assistance (Claude Code). All changes are human-reviewed, covered by unit tests, and the driver has been live-verified against real OTC infrastructure.

Overview

  • Storage backend: OTC EVS (Cinder-compatible block storage)
  • Authentication: AK/SK (same credential pattern as cloud-provider-otc)
  • Deployment: Helm chart (Controller Deployment + Node DaemonSet)
  • Kubernetes compatibility: Works with clusters provisioned by cluster-api-provider-otc with cloud-provider-otc already running (Node providerID + topology labels available). Any cluster whose Nodes carry an otc:///<uuid> providerID and the topology.kubernetes.io/zone label works.

Features

Feature Status
Dynamic provisioning (CreateVolume/DeleteVolume) ✅
Attach/detach (ControllerPublish/Unpublish, OTC-native cloudservers API) ✅
Format + mount (ext4 default, fsType/mountFlags from the StorageClass) ✅
AZ-correct placement (WaitForFirstConsumer topology) ✅
EVS volume types via StorageClass type parameter (default SSD) ✅
Access mode ReadWriteOnce only
Snapshots / restore ❌ planned
Volume expansion (resize) ❌ planned
Raw block volumes ❌ planned
Volume cloning, multi-attach, encryption parameters ❌ not yet scoped

Each deferred feature can be added without breaking the v1 API surface.

Why fresh instead of upstream cinder-csi-plugin

Upstream cinder-csi-plugin authenticates only via Keystone (OpenStack username/password or application-credentials), not AK/SK. Adopting it on OTC would require provisioning a second credential class per cluster, breaking the unified AK/SK model already established by the CCM and CAPI provider. OTC's gophertelekomcloud SDK provides full EVS support via AK/SK (openstack/evs/* APIs), so building a fresh driver is the right fit — following the same reasoning that led to sub-project #1 (CCM skeleton).

Quick start

Driver image

Released images are published to GHCR on every tag:

ghcr.io/d-velop/csi-driver-otc:<version>

To build and push your own:

make docker-build IMG=docker.io/myorg/csi-driver-otc:v0.1.3
make docker-push  IMG=docker.io/myorg/csi-driver-otc:v0.1.3

Render the Helm chart

make helm-template

This outputs the rendered chart YAML to stdout. Customize values in charts/csi-driver-otc/values.yaml as needed (e.g., image repository/tag, node selectors, tolerations).

Configuration

The driver reads cluster credentials from a cloud.conf file embedded in a Kubernetes Secret with key cloud.conf. This Secret must be mounted to the Controller and Node Pods.

Secret format: cloud.conf

The cloud.conf file uses an INI format with a [Global] section, identical to the cloud-provider-otc CCM:

[Global]
access-key   = <AK from otc-credentials Secret>
secret-key   = <SK from otc-credentials Secret>
domain-name  = <OTC domain, e.g., OTC-EU-DE-000000000010000XXXXX>
region       = <OTC region, e.g., eu-de>
project-name = <OTC project, defaults to region if omitted>

Secrets (access-key, secret-key) are never logged or included in error messages.

The otc-credentials principal behind these values needs specific IAM grants beyond the basics (EVS volume access, and — non-obvious, cost two rounds of live debugging to find — ecs:serverVolumeAttachments:* for attach/detach, since the legacy Nova volume-attach endpoint is blocked on OTC regardless of policy). See cloud-provider-otc/docs/context/otc-iam-permissions.md for the full picture.

Example Secret:

apiVersion: v1
kind: Secret
metadata:
  name: csi-otc-cloud-config
  namespace: kube-system
type: Opaque
data:
  cloud.conf: <base64-encoded cloud.conf file>

Deployment to a workload cluster

Follow the same manual delivery pattern that established the CCM (sub-project #2):

  1. Render the chart on your build machine:

    make helm-template > csi-driver-rendered.yaml
  2. Package the chart output into a ConfigMap resource for GitOps delivery:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: csi-driver-otc-chart
      namespace: capi-system  # or your GitOps namespace
    data:
      csi-driver.yaml: |
        <paste rendered csi-driver-rendered.yaml here>
  3. Create the cloud.conf Secret from the cluster's existing otc-credentials Secret (created by cluster-api-provider-otc during cluster provisioning):

    kubectl get secret otc-credentials -n kube-system -o jsonpath='{.data.cloud\.conf}' | base64 -d > /tmp/cloud.conf
    kubectl create secret generic csi-otc-cloud-config \
      --from-file=cloud.conf=/tmp/cloud.conf \
      -n kube-system

    Alternatively, use a csi-cloud-config write analogous to the CCM's reconcileCCMConfig (a follow-up sub-project, not part of this release).

  4. Create a ClusterResourceSet in the sibling cluster-api-provider-otc repo to deliver both resources:

    In cluster-api-provider-otc/config/samples/otc-csi-driver-crs.yaml:

    apiVersion: addons.cluster.x-k8s.io/v1beta1
    kind: ClusterResourceSet
    metadata:
      name: otc-csi-driver
    spec:
      clusterSelector:
        matchLabels:
          csi: "true"  # or your selector
      resources:
      - name: csi-driver-chart
        kind: ConfigMap
        resourceRef:
          apiVersion: v1
          name: csi-driver-otc-chart
      - name: csi-cloud-config
        kind: Secret
        resourceRef:
          apiVersion: v1
          name: csi-otc-cloud-config

    Then apply the CRS to the management cluster to deliver both the chart and Secret to target workload clusters.

Note: ClusterResourceSet authoring and any later automation of csi-cloud-config delivery (analogous to the CCM's per-cluster config automation) are out of scope for this release. They are natural follow-up sub-projects after live verification, exactly as the CCM's own per-cluster config delivery (reconcileCCMConfig) was added post-release. This release focuses on getting the driver itself working, tested, and deliverable as a Helm chart.

Testing

Unit tests use in-memory fakes for the EVS and ECS-attach clients:

go test ./...

All tests pass without requiring a live OTC environment.

Gates (pre-release checklist)

make build        # go build ./...
go vet ./...
gofmt -l .        # must be empty
make test         # go test ./...
make lint         # golangci-lint run
make helm-lint    # helm lint on the chart

Expected result: build clean, gofmt empty, all tests PASS, lint "0 issues", helm lint 0 failed.

Live verification (operator-gated)

Live-verified end-to-end on a real OTC test cluster (2026-07-08): PVC create → EVS volume create → bind → attach → mount → write → detach → delete, full cycle, zero orphaned resources. To repeat:

  1. Deploy the driver via the Helm chart (or manually)
  2. Create a PersistentVolumeClaim using the otc-evs StorageClass
  3. Create a Pod mounting the PVC and write a file
  4. Delete the Pod and PVC
  5. Verify in the OTC console that the EVS volume and its attachment are cleaned up

Two real SDK/API mismatches were found only by this live run, not by unit tests or review, and are now fixed:

  • EVS: the vendored v1 SDK package's Create hardcoded OkCodes that didn't match OTC's actual (async, 202) response, and its Name field used the wrong JSON tag — together they broke CreateVolume's idempotency and orphaned volumes on every retry. Fixed by moving CreateVolume/GetVolume to EVS v3 (job-based) and FindVolumeByName to v2/cloudvolumes.List.
  • Attach/detach: the legacy OpenStack-compatible Nova endpoint (/servers/{id}/os-volume_attachments) is blocked by OTC's policy for regular tenant IAM credentials regardless of grants. Fixed by switching to OTC's native cloudservers attach/detach/list API (pkg/otc/attach.go).

The three correctness gaps noted after the original design review are also now fixed: CreateVolume rejects a same-named volume with a different size/type/AZ as ALREADY_EXISTS; DeleteVolume maps an in-use volume to FAILED_PRECONDITION instead of generic Internal; NodeStageVolume polls for the device path to appear (up to 10s) before calling FormatAndMount.

Contributing

See CONTRIBUTING.md. For security issues, see SECURITY.md.

License

Apache License 2.0

About

OTC-native Kubernetes CSI driver for dynamic EVS PersistentVolumes (AK/SK auth)

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages