api/handler/cleanup_namespace_admission_test.go
Rama main · réplica de lectura del repositorio original.
package handler
import (
"errors"
"path/filepath"
"testing"
"github.com/goodrain/rainbond/db"
"github.com/goodrain/rainbond/db/model"
guard "github.com/goodrain/rainbond/pkg/cleanup"
"github.com/jinzhu/gorm"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
dynamicfake "k8s.io/client-go/dynamic/fake"
ktesting "k8s.io/client-go/testing"
)
func namespaceAdmissionDB(t *testing.T) *gorm.DB {
t.Helper()
database, err := gorm.Open("sqlite3", filepath.Join(t.TempDir(), "ns.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { database.Close() })
database.LogMode(false)
if err := database.AutoMigrate(&model.CleanupStorage{}, &model.CleanupOperation{}).Error; err != nil {
t.Fatal(err)
}
if err := guard.RegisterStorage(database, guard.StorageRegistration{StorageID: "store", Generation: "one", VolumeUID: "volume", RootPath: "/registry"}); err != nil {
t.Fatal(err)
}
if err := database.Model(&model.CleanupStorage{}).Where("storage_id = ?", "store").Update("mode", "ready").Error; err != nil {
t.Fatal(err)
}
return database
}
func TestNamespaceMutationsCannotBypassCleanupAdmission(t *testing.T) {
for _, operation := range []string{"create", "update"} {
for _, blocked := range []bool{false, true} {
t.Run(operation+map[bool]string{false: "-uncertain", true: "-blocked"}[blocked], func(t *testing.T) {
database := namespaceAdmissionDB(t)
previous := db.GetManager()
db.SetTestManager(testManager{Manager: workloadTestManager{database: database}, tenantDao: &testTenantDao{tenant: &model.Tenants{Namespace: "team"}}})
defer db.SetTestManager(previous)
deletion := guard.CoordinationRequest{StorageID: "store", Generation: "one", OperationID: "delete", Owner: "test", Kind: "delete", Scope: "app", Target: "manifest", Fingerprint: "selection"}
if blocked {
if _, err := guard.AcquireOperation(database, deletion); err != nil {
t.Fatal(err)
}
}
client := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme())
calls := 0
client.PrependReactor(operation, "deployments", func(ktesting.Action) (bool, runtime.Object, error) {
calls++
if _, err := guard.AcquireOperation(database, deletion); err != guard.ErrCoordinationBusy {
t.Error("Kubernetes mutation outside admission", err)
}
return true, nil, errors.New("uncertain fixture transport")
})
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{{Group: "apps", Version: "v1"}})
mapper.Add(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}, meta.RESTScopeNamespace)
oldClient, oldMapper := nsResourceDynamicClient, nsResourceRESTMapper
nsResourceDynamicClient = func() dynamic.Interface { return client }
nsResourceRESTMapper = func() meta.RESTMapper { return mapper }
defer func() { nsResourceDynamicClient = oldClient; nsResourceRESTMapper = oldMapper }()
body := []byte("apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: app\n resourceVersion: '1'\n")
if operation == "create" {
_, _, _ = GetNsResourceHandler().CreateNsResource("team", "yaml", body)
} else {
_, _ = GetNsResourceHandler().UpdateNsResource("team", "apps", "v1", "deployments", "app", body)
}
want := 1
if blocked {
want = 0
}
if calls != want {
t.Fatal("wrong external dispatch count", calls, want)
}
if !blocked {
var rows []model.CleanupOperation
if err := database.Where("kind = ?", "producer").Find(&rows).Error; err != nil || len(rows) != 1 || rows[0].State != "uncertain" {
t.Fatal("uncertain effect lost protection", rows, err)
}
}
})
}
}
}
func TestClusterMutationsCannotBypassCleanupAdmission(t *testing.T) {
for _, operation := range []string{"create", "update"} {
for _, blocked := range []bool{false, true} {
t.Run(operation+map[bool]string{false: "-uncertain", true: "-blocked"}[blocked], func(t *testing.T) {
database := namespaceAdmissionDB(t)
previous := db.GetManager()
db.SetTestManager(testManager{Manager: workloadTestManager{database: database}, tenantDao: &testTenantDao{tenant: &model.Tenants{Namespace: "team"}}})
defer db.SetTestManager(previous)
deletion := guard.CoordinationRequest{StorageID: "store", Generation: "one", OperationID: "delete", Owner: "test", Kind: "delete", Scope: "app", Target: "manifest", Fingerprint: "selection"}
if blocked {
if _, err := guard.AcquireOperation(database, deletion); err != nil {
t.Fatal(err)
}
}
client := dynamicfake.NewSimpleDynamicClient(runtime.NewScheme())
calls := 0
client.PrependReactor(operation, "widgets", func(ktesting.Action) (bool, runtime.Object, error) {
calls++
if _, err := guard.AcquireOperation(database, deletion); err != guard.ErrCoordinationBusy {
t.Error("Kubernetes mutation outside admission", err)
}
return true, nil, errors.New("uncertain fixture transport")
})
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{{Group: "example.test", Version: "v1"}})
mapper.Add(schema.GroupVersionKind{Group: "example.test", Version: "v1", Kind: "Widget"}, meta.RESTScopeNamespace)
oldClient, oldMapper := clusterResourceDynamicClient, nsResourceRESTMapper
clusterResourceDynamicClient = func() dynamic.Interface { return client }
nsResourceRESTMapper = func() meta.RESTMapper { return mapper }
defer func() { clusterResourceDynamicClient = oldClient; nsResourceRESTMapper = oldMapper }()
body := []byte("apiVersion: example.test/v1\nkind: Widget\nmetadata:\n name: app\n resourceVersion: '1'\n")
if operation == "create" {
_, _ = GetClusterResourceHandler().CreateResource("example.test", "v1", "widgets", body)
} else {
_, _ = GetClusterResourceHandler().UpdateResource("example.test", "v1", "widgets", "app", body)
}
want := 1
if blocked {
want = 0
}
if calls != want {
t.Fatal("wrong external dispatch count", calls, want)
}
if !blocked {
var rows []model.CleanupOperation
if err := database.Where("kind = ?", "producer").Find(&rows).Error; err != nil || len(rows) != 1 || rows[0].State != "uncertain" {
t.Fatal("uncertain effect lost protection", rows, err)
}
}
})
}
}
}