mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
K8s: Add admission mutation support to builder (#95526)
This commit is contained in:
parent
4508d6c143
commit
c8238c7914
@ -8,11 +8,20 @@ import (
|
||||
)
|
||||
|
||||
type builderAdmission struct {
|
||||
mutators map[schema.GroupVersion]APIGroupMutation
|
||||
validators map[schema.GroupVersion]APIGroupValidation
|
||||
}
|
||||
|
||||
var _ admission.MutationInterface = (*builderAdmission)(nil)
|
||||
var _ admission.ValidationInterface = (*builderAdmission)(nil)
|
||||
|
||||
func (b *builderAdmission) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
|
||||
if m, ok := b.mutators[a.GetResource().GroupVersion()]; ok {
|
||||
return m.Mutate(ctx, a, o)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *builderAdmission) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
|
||||
if v, ok := b.validators[a.GetResource().GroupVersion()]; ok {
|
||||
return v.Validate(ctx, a, o)
|
||||
@ -25,17 +34,22 @@ func (b *builderAdmission) Handles(operation admission.Operation) bool {
|
||||
}
|
||||
|
||||
func NewAdmissionFromBuilders(builders []APIGroupBuilder) *builderAdmission {
|
||||
mutators := make(map[schema.GroupVersion]APIGroupMutation)
|
||||
validators := make(map[schema.GroupVersion]APIGroupValidation)
|
||||
for _, builder := range builders {
|
||||
if m, ok := builder.(APIGroupMutation); ok {
|
||||
mutators[builder.GetGroupVersion()] = m
|
||||
}
|
||||
if v, ok := builder.(APIGroupValidation); ok {
|
||||
validators[builder.GetGroupVersion()] = v
|
||||
}
|
||||
}
|
||||
return NewAdmission(validators)
|
||||
return NewAdmission(mutators, validators)
|
||||
}
|
||||
|
||||
func NewAdmission(validators map[schema.GroupVersion]APIGroupValidation) *builderAdmission {
|
||||
func NewAdmission(mutators map[schema.GroupVersion]APIGroupMutation, validators map[schema.GroupVersion]APIGroupValidation) *builderAdmission {
|
||||
return &builderAdmission{
|
||||
mutators: mutators,
|
||||
validators: validators,
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,10 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
)
|
||||
|
||||
func TestBuilderAdmission_Validate(t *testing.T) {
|
||||
@ -81,7 +83,7 @@ func TestBuilderAdmission_Validate(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b := builder.NewAdmission(tt.validators)
|
||||
b := builder.NewAdmission(nil, tt.validators)
|
||||
err := b.Validate(context.Background(), tt.attributes, nil)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
@ -133,6 +135,72 @@ func TestNewAdmissionFromBuilders(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestBuilderAdmission_Admit(t *testing.T) {
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: "test.grafana.app",
|
||||
Version: "v1",
|
||||
Kind: "Foo",
|
||||
}
|
||||
gvr := gvk.GroupVersion().WithResource("foos")
|
||||
exampleObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: example.PodSpec{}}
|
||||
tests := []struct {
|
||||
name string
|
||||
mutators map[schema.GroupVersion]builder.APIGroupMutation
|
||||
attributes admission.Attributes
|
||||
wantErr bool
|
||||
wantSpec example.PodSpec
|
||||
}{
|
||||
{
|
||||
name: "mutator exists - error",
|
||||
mutators: map[schema.GroupVersion]builder.APIGroupMutation{
|
||||
gvk.GroupVersion(): &mockMutator{
|
||||
mutateFunc: func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return errors.New("test error")
|
||||
},
|
||||
},
|
||||
},
|
||||
attributes: admission.NewAttributesRecord(exampleObj.DeepCopy(), nil, gvk, "default", "foo", gvr, "", admission.Create, nil, false, nil),
|
||||
wantErr: true,
|
||||
wantSpec: exampleObj.Spec,
|
||||
},
|
||||
{
|
||||
name: "mutator exists - add hostname",
|
||||
mutators: map[schema.GroupVersion]builder.APIGroupMutation{
|
||||
gvk.GroupVersion(): &mockMutator{
|
||||
mutateFunc: func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
obj := a.GetObject().(*example.Pod)
|
||||
obj.Spec.Hostname = "test"
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
attributes: admission.NewAttributesRecord(exampleObj.DeepCopy(), nil, gvk, "default", "foo", gvr, "", admission.Create, nil, false, nil),
|
||||
wantErr: false,
|
||||
wantSpec: example.PodSpec{Hostname: "test"},
|
||||
},
|
||||
{
|
||||
name: "mutator does not exist",
|
||||
mutators: map[schema.GroupVersion]builder.APIGroupMutation{},
|
||||
attributes: admission.NewAttributesRecord(exampleObj.DeepCopy(), nil, gvk, "default", "foo", gvr, "", admission.Create, nil, false, nil),
|
||||
wantErr: false,
|
||||
wantSpec: exampleObj.Spec,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
b := builder.NewAdmission(tt.mutators, nil)
|
||||
err := b.Admit(context.Background(), tt.attributes, nil)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.Equal(t, tt.wantSpec, tt.attributes.GetObject().(*example.Pod).Spec)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockBuilder struct {
|
||||
builder.APIGroupBuilder
|
||||
groupVersion schema.GroupVersion
|
||||
@ -147,6 +215,14 @@ func (m *mockBuilder) Validate(ctx context.Context, a admission.Attributes, o ad
|
||||
return m.validator.Validate(ctx, a, o)
|
||||
}
|
||||
|
||||
type mockMutator struct {
|
||||
mutateFunc func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error
|
||||
}
|
||||
|
||||
func (m *mockMutator) Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return m.mutateFunc(ctx, a, o)
|
||||
}
|
||||
|
||||
type mockValidator struct {
|
||||
validateFunc func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error
|
||||
}
|
||||
|
@ -47,7 +47,15 @@ type APIGroupBuilder interface {
|
||||
GetAuthorizer() authorizer.Authorizer
|
||||
}
|
||||
|
||||
type APIGroupMutation interface {
|
||||
// Mutate allows the builder to make changes to the object before it is persisted.
|
||||
// Context is used only for timeout/deadline/cancellation and tracing information.
|
||||
Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error)
|
||||
}
|
||||
|
||||
type APIGroupValidation interface {
|
||||
// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate
|
||||
// Context is used only for timeout/deadline/cancellation and tracing information.
|
||||
Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user