К основному содержанию
скилл CX Codex

admission-control — скилл для Claude Code: валидация и мутация

Помогает писать валидаторы и мутаторы для приложений на grafana-app-sdk: проверка и правка ресурсов до сохранения в Kubernetes.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add grafana/skills --skill admission-control -a claude-code
Открыть на GitHub

Что делает admission-control

Скилл включается, когда вы просите написать валидатор, добавить валидацию или реализовать admission control в приложении на grafana-app-sdk. Он разбирает два вида обработчиков: валидация принимает или отклоняет запрос и не может менять объект, мутация правит ресурс до записи — например, проставляет значения по умолчанию.

Внутри — интерфейсы Validator и Mutator, примеры кода на Go, таблица полей app.AdmissionRequest и типовые шаблоны: проверка неизменяемости поля, сверка полей между собой, проверка ссылки на существующий ресурс. Отдельный раздел про регистрацию обработчиков в pkg/app/app.go через simple.NewApp и ManagedKinds.

Логика обработчиков одинакова для standalone-оператора и приложений внутри grafana/apps, различается только среда выполнения: standalone поднимает свой webhook-сервер, а в grafana/apps обработчики регистрируются как Kubernetes-плагин. Учтите: мутацию и валидацию нужно ещё включить в CUE-описании kind — поля mutation.operations и validation.operations; без этого обработчики не заработают. Для standalone-приложения, если pkg/app/app.go ещё нет, скилл предлагает сгенерировать заготовку командой grafana-app-sdk project component add operator.

Какие задачи решает

  • написать валидатор для своего kind
  • добавить мутирующий webhook с простановкой значений по умолчанию
  • запретить изменение поля после создания ресурса
  • проверить ссылку на существующий ресурс перед сохранением
  • зарегистрировать обработчики admission в приложении

Примеры запросов агенту

  • напиши валидатор для MyKind в grafana-app-sdk
  • добавь мутирующий webhook, который проставляет spec.description по умолчанию
  • как реализовать admission control в приложении на grafana-app-sdk

Как установить admission-control

1Через Skills CLI (Vercel)

npx skills add grafana/skills --skill admission-control -a claude-code

2Вручную: положить файлы

~/.claude/skills/admission-control/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/admission-control/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о admission-control

Как установить admission-control в Claude?

Выполните npx skills add grafana/skills --skill admission-control -a claude-code. Скилл появится в ~/.claude/skills/admission-control/SKILL.md.

Что делает admission-control?

Даёт указания по реализации обработчиков валидации и мутации для приложений на grafana-app-sdk: интерфейсы, примеры на Go, регистрация в приложении.

Чем валидация отличается от мутации?

Валидация только принимает или отклоняет запрос и не может менять ресурс. Мутация правит объект до сохранения, но отклонять запросы лучше в валидаторе.

Исходный текст

SKILL.md · Apache-2.0
---
name: admission-control
license: Apache-2.0
description: Use when the user asks to "write a validator", "add validation", "implement admission control", "write a mutating webhook", "add a mutation handler", "validate incoming resources", "implement admission logic", "add admission webhooks", "write ingress validation", or asks how to validate or mutate resources before they are persisted in a grafana-app-sdk app. Provides guidance on implementing validation and mutation admission handlers for grafana-app-sdk apps.
---

# Admission Control

Admission control intercepts resource create/update requests before they are persisted. In grafana-app-sdk there are two types:

- **Validation** — accept or reject a request; cannot modify the resource
- **Mutation** — modify the resource before it is persisted (e.g. set defaults, normalize fields)

The app business logic for admission is identical whether the app runs as a standalone operator or inside `grafana/apps`. The only difference is the runtime: standalone apps stand up their own webhook server; `grafana/apps` apps have admission auto-registered as a Kubernetes plugin.

## Getting Stubs

For standalone apps, if `pkg/app/app.go` does not yet exist, a stub App can be generated with:

```bash
grafana-app-sdk project component add operator
```

This creates scaffolded `simple.App` which admission handlers can be added to for each kind in `ManagedKinds`.

## Validator Interface

```go
// Implement this interface for each kind you want to validate
type Validator interface {
    Validate(ctx context.Context, request *app.AdmissionRequest) error
}
```

- Return `nil` to admit the request
- Return an error to reject it (the error message is returned to the API caller)
- `app.AdmissionRequest` provides access to the incoming object and operation type
- You can use `k8s.NewAdmissionError(err error, statusCode int, reason string)` (from `"github.com/grafana/grafana-app-sdk/k8s"`) to better control the returned error information

### Validator Example

```go
type MyKindValidator struct{}

func (v *MyKindValidator) Validate(ctx context.Context, req *app.AdmissionRequest) error {
    obj, ok := req.Object.(*v1.MyKind)
    if !ok {
        return fmt.Errorf("admission request object was of invalid type %T (expected *v1.MyKind)", req.Object)
    }

    // Validate spec fields
    if obj.Spec.Title == "" {
        return fmt.Errorf("spec.title is required")
    }

    if obj.Spec.Count < 0 {
        return fmt.Errorf("spec.count must be non-negative, got %d", obj.Spec.Count)
    }

    // Distinguish create vs update
    if req.Action == resource.AdmissionActionUpdate && req.OldObject != nil {
        old, ok := req.OldObject.(*v1.MyKind)
        if !ok {
            return fmt.Errorf("admission request old object was of invalid type %T (expected *v1.MyKind)", req.OldObject)
        }
        if old.Spec.Title != obj.Spec.Title {
            return fmt.Errorf("spec.title is immutable after creation")
        }
    }

    return nil
}
```

## Mutating Admission (Mutator)

```go
// Implement this interface to mutate resources before persistence
type Mutator interface {
    Mutate(ctx context.Context, request *app.AdmissionRequest) (*app.MutatingResponse, error)
}
```

- Return a `MutatingResponse` containing the (optionally modified) object
- Return an error to reject the request entirely
- Best practice is to reject requests from validators, not mutators

### Mutating Handler Example

```go
type MyKindMutator struct{}

func (m *MyKindMutator) Mutate(
    ctx context.Context,
    req *app.AdmissionRequest,
) (*app.MutatingResponse, error) {
    obj, ok := req.Object.(*v1.MyKind)
    if !ok {
        return nil, fmt.Errorf("admission request object was of invalid type %T (expected *v1.MyKind)", req.Object)
    }

    // Set defaults on create
    if req.Action == resource.AdmissionActionCreate {
        if obj.Spec.Description == "" {
            obj.Spec.Description = "No description provided"
        }
    }

    return &app.MutatingResponse{UpdatedObject: obj}, nil
}
```

## Registering Admission Handlers

Register validators and mutators when building the app in `pkg/app/app.go`:

```go
func New(cfg app.Config) (app.App, error) {
    cfg.KubeConfig.APIPath = "/apis"
    a, err := simple.NewApp(simple.AppConfig{
        ManagedKinds: []simple.AppManagedKind{
            {
                Kind:      v1.MyKindKind(),
                Validator: &MyKindValidator{},
                Mutator:   &MyKindMutator{},
            },
        },
    })
    if err != nil {
      return nil, fmt.Errorf("error creating app: %w", err)
    }
    if err = a.ValidateManifest(cfg.ManifestData); err != nil {
        return nil, fmt.Errorf("app manifest validation failed: %w", err)
    }
    return a, nil
}
```

Note that mutation and validation must also be enabled in the kind's CUE definition (`mutation.operations` and `validation.operations` fields) — see the `cue-kind-definition` skill for details.

## Admission Request Fields

Key fields available on `app.AdmissionRequest`:

| Field | Type | Description |
|-------|------|-------------|
| `Object` | `resource.Object` | The incoming resource (after decoding) |
| `OldObject` | `resource.Object` | Previous state (only on UPDATE operations) |
| `Action` | `resource.AdmissionAction` | `AdmissionActionCreate`, `AdmissionActionUpdate`, `AdmissionActionDelete`, `AdmissionActionConnect` |
| `UserInfo` | `resource.AdmissionUserInfo` | The user making the request |
| `Kind` | `string` | The `Object` kind |
| `Group` | `string` | The `Object` API Group |
| `Version` | `string` | The `Object` API Version |

## Validation Patterns

Common patterns to implement:

```go
// Immutability check
if req.Action == resource.AdmissionActionUpdate && old.Spec.ImmutableField != obj.Spec.ImmutableField {
    return fmt.Errorf("spec.immutableField cannot be changed after creation")
}

// Cross-field validation
if obj.Spec.StartTime.After(obj.Spec.EndTime) {
    return fmt.Errorf("spec.startTime must be before spec.endTime")
}

// Referential validation (e.g. check referenced resource exists)
if _, err := v.client.Get(ctx, resource.Identifier{Name: obj.Spec.RefName, Namespace: obj.Namespace}); err != nil {
    return fmt.Errorf("referenced resource %q not found", obj.Spec.RefName)
}
```

## Deployment Difference

| Mode | Admission runtime |
|------|------------------|
| Standalone operator | App starts a webhook server; Kubernetes routes admission requests to it |
| `grafana/apps` | Admission handlers are auto-registered as a Kubernetes in-process plugin — no separate server required |

The handler code itself is identical in both cases.

## Resources

- [grafana-app-sdk GitHub](https://github.com/grafana/grafana-app-sdk)
- [app package docs](https://pkg.go.dev/github.com/grafana/grafana-app-sdk/app)

© grafana · Apache-2.0 · grafana/skills

Похожие скиллы

Всё по теме «Бэкенд и API» →
prometheus-grafana devops-skills Скилл помогает агенту поднять сбор метрик в Prometheus, писать запросы PromQL и собирать панели Grafana. Пригодится, когда в проект нужно добавить мониторинг. CX Codex +1 449установок loki-logging devops-skills Скилл помогает настроить Grafana Loki, Promtail и запросы LogQL прямо в проекте, без похода в документацию. CX Codex +1 330установок dashboard-builder affaan-m Скилл помогает создавать рабочие дашборды для Grafana и SigNoz на основе вопросов дежурного инженера, а не случайного списка метрик. CX Codex +1 4 800установок observability-designer Alireza Rezvani Генерирует дашборды Grafana и находит шумные алерты в конфигах мониторинга. Помогает подготовить базовую наблюдаемость для сервиса перед ростом нагрузки. 671установок monitor-setup Seth Hobson Слэш-команда, которая разворачивает сбор метрик Prometheus, дашборды Grafana, распределённую трассировку и агрегацию логов. CX Codex ★ 41 729звёзд репо grafana-dashboards Seth Hobson Скилл помогает создавать Grafana-дашборды для метрик систем и приложений. Подойдёт для мониторинга, SLO и операционных интерфейсов наблюдаемости. CX Codex 13 097установок

Из того же набора47

private-connectivity Настраивает приватное подключение к Grafana Cloud через AWS PrivateLink, Azure Private Link, GCP Private Service Connect и PDC. Пригодится, когда телеметрия не должна идти через публичный интернет. CX Codex +1 4 100установок ml-ai Настраивает функции машинного обучения и ассистента в Grafana Cloud. Помогает включить прогнозирование метрик, поиск аномалий через DBSCAN и автоанализ инцидентов Sift. CX Codex +1 4 100установок admin Скилл для работы с аккаунтом Grafana Cloud: создаёт стеки, выдаёт токены сервисных аккаунтов, настраивает SSO и RBAC. CX Codex +1 4 200установок plugin-bundle-size Помогает уменьшить размер входного файла module.js в плагинах приложений Grafana через React.lazy и разделение чанков в Webpack. CX Codex +1 4 200установок grafana-scenes Скилл для сборки страниц плагинов Grafana на фреймворке @grafana/scenes: панели, переменные, drilldown-навигация. CX Codex +1 4 200установок your-skill-name Шаблонный скилл от Grafana для создания инструкций агентам с PromQL-примерами и разметкой по спецификации Agent Skills. CX Codex +1 3 800установок prometheus-cardinality-troubleshooter Диагностирует активные проблемы с кардинальностью Prometheus: рост памяти, медленные запросы, ошибки приёма метрик и всплески счёта. CX Codex +1 3 800установок skill-authoring Помогает писать и проверять SKILL.md по рубрике из четырёх критериев, которую использует CI-гейт grafana/skills. Пригодится, когда скилл не срабатывает или не проходит ревью. CX Codex +1 3 800установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой