mcpField
A
admission-control

admission-control

github.com/grafana开发2954.2k 次安装1 天前更新
代码仓库

关于 admission-control

Admission Control 介绍如何为 grafana-app-sdk 管理的资源实现校验和变更处理器,面向需要在持久化前拒绝请求或调整对象的应用开发者。独立运行与 grafana/apps 运行时使用相同的处理器逻辑。

主要功能

  1. 01校验创建和更新请求
  2. 02在持久化前变更资源
  3. 03读取请求对象及操作详情
  4. 04为受管类型注册处理器
  5. 05支持独立和 grafana/apps 运行时

适用场景

  1. 01拒绝无效的资源请求
  2. 02设置默认值或规范化资源字段
  3. 03在更新时实施不可变性规则

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/grafana/skills --skill admission-control

安装后在 Claude Code 中输入/admission-control确认已加载。

附带文件references/admission-patterns.md

详细介绍

准入控制

准入控制会在资源创建或更新请求持久化之前拦截这些请求。在 grafana-app-sdk 中有两种类型:

  • 验证 — 接受或拒绝请求;不能修改资源
  • 变更 — 在资源持久化之前修改资源(例如,设置默认值、规范化字段)

无论应用作为独立 Operator 运行,还是在 grafana/apps 中运行,其准入业务逻辑都相同。唯一的区别是运行时:独立应用会启动自己的 Webhook 服务;grafana/apps 应用则会将准入处理程序自动注册为 Kubernetes 插件。

获取存根

对于独立应用,如果 pkg/app/app.go 尚不存在,可以生成一个 App 存根:

grafana-app-sdk project component add operator

这会创建脚手架 simple.App,可以为 ManagedKinds 中的每种类型添加准入处理程序。

验证器接口

// Implement this interface for each kind you want to validate
type Validator interface {
    Validate(ctx context.Context, request *app.AdmissionRequest) error
}
  • 返回 nil 以准入请求
  • 返回错误以拒绝请求(错误消息会返回给 API 调用方)
  • app.AdmissionRequest 可用于访问传入对象和操作类型
  • 可以使用 k8s.NewAdmissionError(err error, statusCode int, reason string)(来自 "github.com/grafana/grafana-app-sdk/k8s")更好地控制返回的错误信息
验证器示例
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
}

变更型准入(变更器)

// Implement this interface to mutate resources before persistence
type Mutator interface {
    Mutate(ctx context.Context, request *app.AdmissionRequest) (*app.MutatingResponse, error)
}
  • 返回包含(可选修改后的)对象的 MutatingResponse
  • 返回错误以完全拒绝请求
  • 最佳实践是通过验证器拒绝请求,而不是通过变更器
变更处理程序示例
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
}

注册准入处理程序

在 pkg/app/app.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
}

请注意,还必须在类型的 CUE 定义中启用变更和验证(mutation.operations 和 validation.operations 字段)——详情请参阅 cue-kind-definition 技能。

准入请求字段

app.AdmissionRequest 上可用的关键字段:

字段类型描述
Objectresource.Object传入的资源(解码后)
OldObjectresource.Object先前状态(仅适用于 UPDATE 操作)
Actionresource.AdmissionActionAdmissionActionCreate、AdmissionActionUpdate、AdmissionActionDelete、AdmissionActionConnect
UserInforesource.AdmissionUserInfo发起请求的用户
KindstringObject 类型
GroupstringObject API 组
VersionstringObject API 版本

验证模式

要实现的常见模式:

// 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)
}

部署差异

模式准入运行时
独立 Operator应用会启动 Webhook 服务;Kubernetes 会将准入请求路由到该服务
grafana/apps准入处理程序会自动注册为 Kubernetes 进程内插件——无需单独的服务

两种情况下的处理程序代码本身完全相同。

资源

评论

支持 Markdown