Ctrl K
ComunidadGuías…

api/handler/cleanup_namespace_admission_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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)
					}
				}
			})
		}
	}
}