Kubernetes · Arazzo Workflow

Kubernetes Rotate a Secret and Restart Its Consumers

Version 1.0.0

Inventory existing secrets, write the new credential, then roll the deployment that consumes it.

1 workflow 1 source API 1 provider
View Spec View on GitHub AutomationCloud NativeCNCFContainersDeploymentOpen SourceOrchestrationScalingArazzoWorkflows

Provider

kubernetes

Workflows

rotate-secret
Write a rotated Secret and roll the deployment that consumes it.
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.
4 steps inputs: appName, deploymentName, image, namespace, replicas, rotationStamp, secretName, secretStringData, secretType outputs: deploymentName, previousSecrets, rolledGeneration, secretName
1
inventorySecrets
listNamespacedSecrets
List the namespace's secrets to record what exists before the rotation, which gives the run an auditable before state.
2
writeRotatedSecret
createNamespacedSecret
Write the rotated credential using stringData so plain-text values are encoded by the API server rather than by the caller.
3
readConsumer
getNamespacedDeployment
Read the deployment that consumes the secret and capture its resourceVersion so the restart write is concurrency-safe.
4
rollConsumer
replaceNamespacedDeployment
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.

Source API Descriptions

Arazzo Workflow Specification

Raw ↑
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