arazzo: 1.0.1 info: title: Kubernetes Rotate a Secret and Restart Its Consumers summary: Inventory existing secrets, write the new credential, then roll the deployment that consumes it. description: >- Rotating a credential is only half the job — pods that mounted the old Secret keep the old value until they restart. The workflow lists the namespace's secrets to record what is in place, writes the rotated Secret, reads the consuming deployment, and replaces it with a changed rollout annotation so the deployment controller performs a rolling restart and every pod picks up the new credential. Every step spells out its request inline so the flow can be read and executed without opening the underlying OpenAPI description. version: 1.0.0 sourceDescriptions: - name: kubernetesApi url: ../openapi/kubernetes-api-openapi.yml type: openapi workflows: - workflowId: rotate-secret summary: Write a rotated Secret and roll the deployment that consumes it. description: >- Inventories the namespace's secrets, creates the rotated Secret, reads the consuming deployment, and replaces it with a new rollout annotation to force a rolling restart so pods pick up the new value. inputs: type: object required: - namespace - secretName - secretStringData - deploymentName - appName - image - replicas - rotationStamp properties: namespace: type: string description: The namespace holding the secret and its consuming deployment. secretName: type: string description: Name of the Secret to write. secretStringData: type: object description: Plain-text key/value pairs; the API server base64-encodes them on write. secretType: type: string description: Secret type — Opaque, kubernetes.io/tls, or kubernetes.io/dockerconfigjson. deploymentName: type: string description: The deployment whose pods consume the secret. appName: type: string description: The app label value used by the deployment selector and pod template. image: type: string description: Container image to preserve on the replaced deployment. replicas: type: integer description: Replica count to preserve on the replaced deployment. rotationStamp: type: string description: >- Value written to the rollout annotation to force a restart. Any value that changes per rotation works, such as a timestamp. steps: - stepId: inventorySecrets description: >- List the namespace's secrets to record what exists before the rotation, which gives the run an auditable before state. operationId: listNamespacedSecrets parameters: - name: namespace in: path value: $inputs.namespace - name: limit in: query value: 100 successCriteria: - condition: $statusCode == 200 outputs: secrets: $response.body#/items - stepId: writeRotatedSecret description: >- Write the rotated credential using stringData so plain-text values are encoded by the API server rather than by the caller. operationId: createNamespacedSecret parameters: - name: namespace in: path value: $inputs.namespace requestBody: contentType: application/json payload: apiVersion: v1 kind: Secret metadata: name: $inputs.secretName namespace: $inputs.namespace labels: app: $inputs.appName annotations: rotated-at: $inputs.rotationStamp type: $inputs.secretType stringData: $inputs.secretStringData successCriteria: - condition: $statusCode == 201 outputs: secretName: $response.body#/metadata/name secretResourceVersion: $response.body#/metadata/resourceVersion - stepId: readConsumer description: >- Read the deployment that consumes the secret and capture its resourceVersion so the restart write is concurrency-safe. operationId: getNamespacedDeployment parameters: - name: namespace in: path value: $inputs.namespace - name: name in: path value: $inputs.deploymentName successCriteria: - condition: $statusCode == 200 outputs: deploymentName: $response.body#/metadata/name resourceVersion: $response.body#/metadata/resourceVersion containerPort: $response.body#/spec/template/spec/containers/0/ports/0/containerPort - stepId: rollConsumer description: >- Replace the deployment with a changed pod-template annotation. The template change is what triggers the rolling restart; pods remount the secret and read the rotated value on start. operationId: replaceNamespacedDeployment parameters: - name: namespace in: path value: $inputs.namespace - name: name in: path value: $steps.readConsumer.outputs.deploymentName requestBody: contentType: application/json payload: apiVersion: apps/v1 kind: Deployment metadata: name: $inputs.deploymentName namespace: $inputs.namespace resourceVersion: $steps.readConsumer.outputs.resourceVersion labels: app: $inputs.appName spec: replicas: $inputs.replicas selector: matchLabels: app: $inputs.appName strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 template: metadata: labels: app: $inputs.appName annotations: secret-rotated-at: $inputs.rotationStamp spec: containers: - name: $inputs.appName image: $inputs.image imagePullPolicy: IfNotPresent env: - name: SECRET_ROTATED_AT value: $inputs.rotationStamp successCriteria: - condition: $statusCode == 200 outputs: rolledGeneration: $response.body#/metadata/generation rolledResourceVersion: $response.body#/metadata/resourceVersion outputs: previousSecrets: $steps.inventorySecrets.outputs.secrets secretName: $steps.writeRotatedSecret.outputs.secretName deploymentName: $steps.readConsumer.outputs.deploymentName rolledGeneration: $steps.rollConsumer.outputs.rolledGeneration