• Русский
  • Manual Approval Gate для Tekton Pipelines

    Обзор функции

    Manual Approval Gate позволяет авторам pipeline приостанавливать PipelineRun, пока назначенные approvers не проверят и не одобрят операцию. На уровне реализации контроллер, предоставляемый Operator, создает ресурс ApprovalTask для каждого шага одобрения, отслеживает ответы approvers и записывает результат обратно в исходный CustomRun.

    Сценарии использования

    • Обязательное прохождение контрольных точек с участием человека перед развертыванием в production или выполнением разрушительных операций по обслуживанию.
    • Реализация политик многопользовательского одобрения путем объединения отдельных пользователей и групп с помощью numberOfApprovalsRequired.
    • Аудит того, кто одобрил или отклонил изменение, с помощью запросов к полям статуса ApprovalTask или вывода CLI.

    Предварительные требования

    • Администратор кластера развернул Manual Approval Gate. Если нет, см. руководство по развертыванию.
    • Вы можете создавать или редактировать объекты Pipeline/PipelineRun в целевом namespace.
    • Approvers могут выполнять patch для ресурсов approvaltasks.openshift-pipelines.org (обычно через kubectl) в целевом namespace. Платформы, такие как Alauda Container Platform, предоставляют эту возможность по умолчанию; настраивайте RBAC только в том случае, если вы явно удалили встроенные permissions.

    Шаги

    1. Объявите шаг одобрения в pipeline

    apiVersion: tekton.dev/v1
    kind: Pipeline
    metadata:
      name: deploy-with-approval
    spec:
      tasks:
        - name: build
          taskRef:
            name: build-bundle     # Placeholder Task; replace with your build logic
        - name: wait-for-approval
          runAfter:
            - build
          taskRef:
            apiVersion: openshift-pipelines.org/v1alpha1
            kind: ApprovalTask
          timeout: "24h"
          params:
            - name: approvers
              value:
                - alice
                - group:release-managers
            - name: numberOfApprovalsRequired
              value: "1"
            - name: description
              value: "Approve promotion into production"
        - name: deploy
          runAfter:
            - wait-for-approval
          taskRef:
            name: deploy-prod     # Placeholder Task; replace with your deployment logic

    Когда pipeline достигает wait-for-approval, Tekton создает CustomRun. Контроллер одобрения создает ApprovalTask с вашими параметрами и оставляет PipelineRun в состоянии ожидания, пока не будет достигнут quorum или не произойдет отклонение.

    2. Контролируйте статус одобрения

    $ kubectl get approvaltasks.openshift-pipelines.org
    
    NAME                                         AGE
    deploy-with-approval-run-wait-for-approval   4m16s
    $ kubectl describe approvaltask deploy-with-approval-run-wait-for-approval
    Name:         deploy-with-approval-run-wait-for-approval
    Status:
      Approvals Received:  1
      Approvals Required:  1
      Approvers:
        alice
        release-managers
      Approvers Response:
        Name:      alice
        Response:  approved
        Type:      User
      Start Time:  2025-11-17T10:37:01Z
      State:       approved
    Events:        <none>

    Важные поля статуса:

    • status.state: общее состояние gate, например pending, approved или rejected.
    • status.approvalsRequired / status.approvalsReceived: отслеживание quorum (счетчик received появляется только после ответа хотя бы одного approver).
    • status.approversResponse: результат по каждому пользователю/группе и сообщения, полезные для аудита.

    3. Одобрите или отклоните

    Одобрение пользователем

    Сначала проверьте список approvers, чтобы определить правильный индекс:

    $ kubectl get approvaltask deploy-with-approval-run-wait-for-approval -o json | jq '.spec.approvers'
    [
      {
        "name": "alice",
        "type": "User",
        "input": "pending",
      },
      {
        "name": "release-managers",
        "type": "Group",
        "input": "pending",
        "users": []
      }
    ]

    Чтобы одобрить как пользователь:

    $ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
        --type='json' \
        --as alice \
        -p='[
          {"op":"replace","path":"/spec/approvers/0/input","value":"approve"},
          {"op":"replace","path":"/spec/approvers/0/message","value":"QA complete"}
        ]'

    Чтобы отклонить как пользователь:

    $ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
        --type='json' \
        --as alice \
        -p='[
          {"op":"replace","path":"/spec/approvers/0/input","value":"reject"},
          {"op":"replace","path":"/spec/approvers/0/message","value":"Found regression"}
        ]'

    Одобрение группой

    Если группа настроена как approver, отдельные участники этой группы могут отправлять свое одобрение, добавляя ответ в массив users внутри записи группы. Несколько участников группы могут одобрять независимо друг от друга, и каждое одобрение засчитывается в общий итог approvalsReceived.

    Изначально у записи approver для группы нет поля users:

    spec:
      approvers:
      - name: alice
        type: User
        input: pending
        message: ""
      - name: release-managers
        type: Group
        input: pending
        message: ""
      numberOfApprovalsRequired: 2

    Первый участник группы создает массив users и устанавливает input группы в approve:

    # First member (bob) from release-managers group approves
    $ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
        --type='json' \
        --as bob \
        --as-group release-managers \
        -p='[
          {"op":"add","path":"/spec/approvers/1/users","value":[{"name":"bob","input":"approve"}]},
          {"op":"replace","path":"/spec/approvers/1/input","value":"approve"},
          {"op":"replace","path":"/spec/approvers/1/message","value":"Approved by bob"}
        ]'

    Последующие участники группы добавляются в существующий массив users и обновляют input группы:

    # Second member (carol) from release-managers group also approves
    $ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
        --type='json' \
        --as carol \
        --as-group release-managers \
        -p='[
          {"op":"add","path":"/spec/approvers/1/users/-","value":{"name":"carol","input":"approve"}},
          {"op":"replace","path":"/spec/approvers/1/input","value":"approve"},
          {"op":"replace","path":"/spec/approvers/1/message","value":"Approved by carol"}
        ]'

    Чтобы отклонить как участник группы:

    # A member (david) from release-managers group rejects
    $ kubectl patch approvaltask deploy-with-approval-run-wait-for-approval \
        --type='json' \
        --as david \
        --as-group release-managers \
        -p='[
          {"op":"add","path":"/spec/approvers/1/users/-","value":{"name":"david","input":"reject"}},
          {"op":"replace","path":"/spec/approvers/1/input","value":"reject"},
          {"op":"replace","path":"/spec/approvers/1/message","value":"Security concerns found"}
        ]'

    После этих patch-операций проверьте запись группы и статус, чтобы увидеть ответы всех участников:

    $ kubectl get approvaltask deploy-with-approval-run-wait-for-approval -o json | jq '{spec: .spec.approvers[1], status: .status}'
    {
      "spec": {
        "input": "approve",
        "message": "Approved by carol",
        "name": "release-managers",
        "type": "Group",
        "users": [
          {
            "input": "approve",
            "name": "bob"
          },
          {
            "input": "approve",
            "name": "carol"
          }
        ]
      },
      "status": {
        "approvalsReceived": 2,
        "approvalsRequired": 2,
        "approvers": [
          "alice",
          "release-managers"
        ],
        "approversResponse": [
          {
            "groupMembers": [
              {
                "message": "Approved by carol",
                "name": "bob",
                "response": "approved"
              },
              {
                "message": "Approved by carol",
                "name": "carol",
                "response": "approved"
              }
            ],
            "message": "Approved by carol",
            "name": "release-managers",
            "response": "approved",
            "type": "Group"
          }
        ],
        "startTime": "2025-11-18T14:07:48Z",
        "state": "approved"
      }
    }

    В этом примере и bob, и carol из группы release-managers одобрили запрос. Каждое одобрение от участника группы увеличивает approvalsReceived отдельно, поэтому два одобрения от участников группы считаются как два одобрения в требуемом общем количестве. Поле status.approversResponse показывает подробные сведения об одобрении, включая ответы отдельных участников группы.

    Ключевые моменты для одобрения группой:

    • Каждый участник группы должен выполнить две обязательные операции: добавить свою запись в массив users И установить input группы (либо approve, либо reject). При необходимости он также может задать message группы.
    • Первый участник группы создает массив users, используя path /spec/approvers/<index>/users со значением массива.
    • Последующие участники добавляются в массив с помощью path /spec/approvers/<index>/users/-, где - добавляет элемент в конец массива.
    • Каждая запись пользователя в массиве users содержит только поля name и input (в записи пользователя нет поля message).
    • Поле message на уровне группы является необязательным и общим; оно будет перезаписано последующими ответами, если они зададут новое сообщение.
    • Каждое одобрение участника группы отдельно увеличивает approvalsReceived.
    • Несколько участников одной и той же группы могут одобрять, и каждый такой ответ засчитывается в требуемый общий итог.
    • Поле status.approversResponse отслеживает подробную информацию об одобрении, включая отдельных участников группы.
    • Используйте --as <username> --as-group <groupname>, чтобы идентифицировать себя как участника группы при patch-операциях.

    Контроллер устанавливает соответствующий CustomRun и PipelineRun в состояние Succeeded или Failed соответственно: одобрения накапливаются, пока не будет выполнено numberOfApprovalsRequired, тогда как любое отклонение немедленно завершает этот участок pipeline с ошибкой.

    Совет: Используйте --as <username> (обязательно) и --as-group <group>, когда нужно одобрить от имени конкретной идентичности. Validation webhook позволяет изменять только ту запись, которая соответствует impersonated user и группе. RBAC должен предоставлять вам права impersonation. Например, kubectl patch ... --as bob --as-group release-managers идентифицирует вас как пользователя bob, действующего в группе release-managers.

    4. Увеличьте timeouts PipelineRun для длительных одобрений

    Если одобрение может занять часы или дни, настройте PipelineRun.spec.timeouts.pipeline и PipelineRun.spec.timeouts.tasks так, чтобы они превышали окно одобрения, и тогда run не завершится до ответа approvers. Простой PipelineRun для проверки gate одобрения выглядит так:

    apiVersion: tekton.dev/v1
    kind: PipelineRun
    metadata:
      name: deploy-with-approval-run
    spec:
      pipelineRef:
        name: deploy-with-approval
      timeouts:
        pipeline: 72h
        tasks: 72h

    Убедитесь, что параметр timeout для approval task меньше, чем timeout pipeline. Иначе PipelineRun может истечь раньше, оставив одобрение незавершенным.

    Результаты выполнения

    • kubectl get approvaltasks -o yaml показывает каждый gate одобрения с полем state и полями, связанными с quorum (столбец approvalsReceived появляется после ответа кого-либо).
    • Статус PipelineRun отражает результат одобрения: при одобрении downstream tasks возобновляют выполнение; при отклонении run завершается с ошибкой, а причина передается из ApprovalTask.
    • Логи dispatch или вывод kubectl get approvaltask -o yaml предоставляют историю одобрения для аудита.

    Устранение неполадок

    • Approval task не появляется: убедитесь, что администраторский ManualApprovalGate CR находится в состоянии READY. Без контроллера объекты CustomRun останутся в ожидании.
    • У approvers недостаточно permissions: предоставьте им доступ get, list, update и patch к approvaltasks.openshift-pipelines.org в соответствующем namespace.
    • Pipeline завершился до окончания одобрения: задайте PipelineRun.spec.timeouts.pipeline и PipelineRun.spec.timeouts.tasks так, чтобы они покрывали ожидаемое окно одобрения, и убедитесь, что timeout одобрения реалистичен. Иначе run может завершиться по timeout, даже если approvers еще не ответили.
    • Застрял в pending даже после одобрений: проверьте status.approversResponse на наличие пользователей, которые изменили свой голос или отклонили запрос. Возможно, потребуется обновить список approvers и повторно запустить pipeline.

    Идентификаторы пользователей и групп

    Manual Approval Gate использует identity provider вашей платформы для сопоставления имен approvers. Всегда используйте канонические идентификаторы, предоставляемые provider, а не отображаемые имена из UI. Например, в Alauda Container Platform:

    $ kubectl get users.auth.alauda.io
    NAME                               TYPE    USERNAME   AGE
    21232f297a57a5a743894a0e4a801fc3   local   admin      19d

    Используйте столбец USERNAME (например, admin) при добавлении user approvers.

    $ kubectl get groups.auth.alauda.io
    NAME      DISPLAYNAME   CONNTYPE   CONNID   AGE
    g-v9mfs   test-group    local      local    19d

    Используйте столбец NAME (например, g-v9mfs) при указании group approvers (например, group:g-v9mfs). На других платформах доступны аналогичные ресурсы — обратитесь к документации identity service, чтобы уточнить точные имена полей.

    Подробнее