diff --git a/pkg/core/bootstrap/bootstrap.go b/pkg/core/bootstrap/bootstrap.go index ecd111257967..06add90beef6 100644 --- a/pkg/core/bootstrap/bootstrap.go +++ b/pkg/core/bootstrap/bootstrap.go @@ -10,6 +10,7 @@ import ( "github.com/Kong/kuma/pkg/core/resources/apis/mesh" core_manager "github.com/Kong/kuma/pkg/core/resources/manager" core_model "github.com/Kong/kuma/pkg/core/resources/model" + "github.com/Kong/kuma/pkg/core/resources/registry" core_runtime "github.com/Kong/kuma/pkg/core/runtime" runtime_reports "github.com/Kong/kuma/pkg/core/runtime/reports" secret_cipher "github.com/Kong/kuma/pkg/core/secrets/cipher" @@ -219,11 +220,10 @@ func initializeBuiltinCaManager(builder *core_runtime.Builder) { func initializeResourceManager(builder *core_runtime.Builder) { defaultManager := core_manager.NewResourceManager(builder.ResourceStore()) - meshManager := mesh_managers.NewMeshManager(builder.ResourceStore(), builder.BuiltinCaManager()) - customManagers := map[core_model.ResourceType]core_manager.ResourceManager{ - mesh.MeshType: meshManager, - } + customManagers := map[core_model.ResourceType]core_manager.ResourceManager{} customizableManager := core_manager.NewCustomizableResourceManager(defaultManager, customManagers) + meshManager := mesh_managers.NewMeshManager(builder.ResourceStore(), builder.BuiltinCaManager(), customizableManager, builder.SecretManager(), registry.Global()) + customManagers[mesh.MeshType] = meshManager builder.WithResourceManager(customizableManager) } diff --git a/pkg/core/managers/apis/mesh/mesh_manager.go b/pkg/core/managers/apis/mesh/mesh_manager.go index d019849bd1f9..489f3965b99b 100644 --- a/pkg/core/managers/apis/mesh/mesh_manager.go +++ b/pkg/core/managers/apis/mesh/mesh_manager.go @@ -11,19 +11,33 @@ import ( core_mesh "github.com/Kong/kuma/pkg/core/resources/apis/mesh" core_manager "github.com/Kong/kuma/pkg/core/resources/manager" core_model "github.com/Kong/kuma/pkg/core/resources/model" + core_registry "github.com/Kong/kuma/pkg/core/resources/registry" core_store "github.com/Kong/kuma/pkg/core/resources/store" + secrets_manager "github.com/Kong/kuma/pkg/core/secrets/manager" ) -func NewMeshManager(store core_store.ResourceStore, builtinCaManager builtin_ca.BuiltinCaManager) core_manager.ResourceManager { +func NewMeshManager( + store core_store.ResourceStore, + builtinCaManager builtin_ca.BuiltinCaManager, + otherManagers core_manager.ResourceManager, + secretManager secrets_manager.SecretManager, + registry core_registry.TypeRegistry, +) core_manager.ResourceManager { return &meshManager{ store: store, builtinCaManager: builtinCaManager, + otherManagers: otherManagers, + secretManager: secretManager, + registry: registry, } } type meshManager struct { store core_store.ResourceStore builtinCaManager builtin_ca.BuiltinCaManager + otherManagers core_manager.ResourceManager + secretManager secrets_manager.SecretManager + registry core_registry.TypeRegistry } func (m *meshManager) Get(ctx context.Context, resource core_model.Resource, fs ...core_store.GetOptionsFunc) error { @@ -81,14 +95,51 @@ func (m *meshManager) Delete(ctx context.Context, resource core_model.Resource, } // delete Mesh first to avoid a state where a Mesh could exist without a Built-in CA. // even if removal of Built-in CA fails later on, delete opration can be safely tried again. + var notFoundErr error if err := m.store.Delete(ctx, mesh, fs...); err != nil { - return err + if core_store.IsResourceNotFound(err) { + notFoundErr = err + } else { // ignore other errors so we can retry removing other resources + return err + } } + opts := core_store.NewDeleteOptions(fs...) // delete CA name := core_store.NewDeleteOptions(fs...).Mesh - if err := m.builtinCaManager.Delete(ctx, name); err != nil { + if err := m.builtinCaManager.Delete(ctx, name); err != nil && !core_store.IsResourceNotFound(err) { return errors.Wrapf(err, "failed to delete Builtin CA for a given mesh") } + // delete all other secrets + if err := m.secretManager.DeleteAll(ctx, core_store.DeleteAllByMesh(opts.Mesh)); err != nil { + return errors.Wrap(err, "could not delete associated secrets") + } + // delete other resources associated by mesh + for _, typ := range m.registry.ListTypes() { + list, err := m.registry.NewList(typ) + if err != nil { + return err + } + if err := m.otherManagers.DeleteAll(ctx, list, core_store.DeleteAllByMesh(opts.Name)); err != nil { + return errors.Wrap(err, "could not delete associated resources") + } + } + return notFoundErr +} + +func (m *meshManager) DeleteAll(ctx context.Context, list core_model.ResourceList, fs ...core_store.DeleteAllOptionsFunc) error { + meshes, err := m.meshes(list) + if err != nil { + return err + } + opts := core_store.NewDeleteAllOptions(fs...) + if err := m.List(ctx, list, core_store.ListByMesh(opts.Mesh)); err != nil { + return err + } + for _, item := range meshes.Items { + if err := m.Delete(ctx, item, core_store.DeleteBy(core_model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) { + return err + } + } return nil } diff --git a/pkg/core/managers/apis/mesh/mesh_manager_suite_test.go b/pkg/core/managers/apis/mesh/mesh_manager_suite_test.go new file mode 100644 index 000000000000..362af0d48fbd --- /dev/null +++ b/pkg/core/managers/apis/mesh/mesh_manager_suite_test.go @@ -0,0 +1,13 @@ +package mesh + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestMeshManager(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Mesh Manager Suite") +} diff --git a/pkg/core/managers/apis/mesh/mesh_manager_test.go b/pkg/core/managers/apis/mesh/mesh_manager_test.go new file mode 100644 index 000000000000..4f84d4510415 --- /dev/null +++ b/pkg/core/managers/apis/mesh/mesh_manager_test.go @@ -0,0 +1,118 @@ +package mesh + +import ( + "context" + "github.com/Kong/kuma/pkg/core/ca/builtin" + core_mesh "github.com/Kong/kuma/pkg/core/resources/apis/mesh" + "github.com/Kong/kuma/pkg/core/resources/manager" + "github.com/Kong/kuma/pkg/core/resources/model" + "github.com/Kong/kuma/pkg/core/resources/store" + "github.com/Kong/kuma/pkg/core/secrets/cipher" + secrets_manager "github.com/Kong/kuma/pkg/core/secrets/manager" + secrets_store "github.com/Kong/kuma/pkg/core/secrets/store" + "github.com/Kong/kuma/pkg/plugins/resources/memory" + test_resources "github.com/Kong/kuma/pkg/test/resources" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Mesh Manager", func() { + + const namespace = "default" + + var resManager manager.ResourceManager + var resStore store.ResourceStore + var caManager builtin.BuiltinCaManager + + BeforeEach(func() { + resStore = memory.NewStore() + secretManager := secrets_manager.NewSecretManager(secrets_store.NewSecretStore(resStore), cipher.None()) + caManager = builtin.NewBuiltinCaManager(secretManager) + manager := manager.NewResourceManager(resStore) + + resManager = NewMeshManager(resStore, caManager, manager, secretManager, test_resources.Global()) + }) + + Describe("Create()", func() { + It("should also create a built-in CA", func() { + // given + meshName := "mesh-1" + resKey := model.ResourceKey{ + Mesh: meshName, + Namespace: namespace, + Name: meshName, + } + + // when + mesh := core_mesh.MeshResource{} + err := resManager.Create(context.Background(), &mesh, store.CreateBy(resKey)) + + // then + Expect(err).ToNot(HaveOccurred()) + + // and built-in CA is created + certs, err := caManager.GetRootCerts(context.Background(), meshName) + Expect(err).ToNot(HaveOccurred()) + Expect(certs).To(HaveLen(1)) + }) + }) + + Describe("Delete()", func() { + It("should delete all associated resources", func() { + // given mesh + meshName := "mesh-1" + + mesh := core_mesh.MeshResource{} + resKey := model.ResourceKey{ + Mesh: meshName, + Namespace: namespace, + Name: meshName, + } + err := resManager.Create(context.Background(), &mesh, store.CreateBy(resKey)) + Expect(err).ToNot(HaveOccurred()) + + // and resource associated with it + dp := core_mesh.DataplaneResource{} + err = resStore.Create(context.Background(), &dp, store.CreateByKey(namespace, "dp-1", meshName)) + Expect(err).ToNot(HaveOccurred()) + + // when mesh is deleted + err = resManager.Delete(context.Background(), &mesh, store.DeleteBy(resKey)) + + // then + Expect(err).ToNot(HaveOccurred()) + + // and resource is deleted + err = resStore.Get(context.Background(), &core_mesh.DataplaneResource{}, store.GetByKey(namespace, "dp-1", meshName)) + Expect(store.IsResourceNotFound(err)).To(BeTrue()) + + // and built-in mesh CA is deleted + _, err = caManager.GetRootCerts(context.Background(), meshName) + Expect(err).ToNot(BeNil()) + Expect(err.Error()).To(Equal("failed to load CA key pair for Mesh \"mesh-1\": Resource not found: type=\"Secret\" namespace=\"default\" name=\"builtinca.mesh-1\" mesh=\"mesh-1\"")) + }) + + It("should delete all associated resources even if mesh is already removed", func() { + // given resource that was not deleted with mesh + dp := core_mesh.DataplaneResource{} + dpKey := model.ResourceKey{ + Mesh: "already-deleted", + Namespace: namespace, + Name: "dp-1", + } + err := resStore.Create(context.Background(), &dp, store.CreateBy(dpKey)) + Expect(err).ToNot(HaveOccurred()) + + // when + mesh := core_mesh.MeshResource{} + err = resManager.Delete(context.Background(), &mesh, store.DeleteByKey(namespace, "already-deleted", "already-deleted")) + + // then + Expect(err).ToNot(HaveOccurred()) + + // and resource is deleted + err = resStore.Get(context.Background(), &dp, store.GetBy(dpKey)) + Expect(store.IsResourceNotFound(err)).To(BeTrue()) + }) + }) +}) diff --git a/pkg/core/resources/apis/mesh/dataplane_overview.go b/pkg/core/resources/apis/mesh/dataplane_overview.go index 801af5f2cf08..c426a3f9e461 100644 --- a/pkg/core/resources/apis/mesh/dataplane_overview.go +++ b/pkg/core/resources/apis/mesh/dataplane_overview.go @@ -2,8 +2,6 @@ package mesh import ( "errors" - "github.com/Kong/kuma/pkg/core/resources/registry" - mesh_proto "github.com/Kong/kuma/api/mesh/v1alpha1" "github.com/Kong/kuma/pkg/core/resources/model" ) @@ -74,11 +72,6 @@ func (l *DataplaneOverviewResourceList) AddItem(r model.Resource) error { } } -func init() { - registry.RegisterType(&DataplaneOverviewResource{}) - registry.RegistryListType(&DataplaneOverviewResourceList{}) -} - func NewDataplaneOverviews(dataplanes DataplaneResourceList, insights DataplaneInsightResourceList) DataplaneOverviewResourceList { insightsByKey := map[model.ResourceKey]*DataplaneInsightResource{} for _, insight := range insights.Items { diff --git a/pkg/core/resources/apis/system/secret.go b/pkg/core/resources/apis/system/secret.go index 3be904aaa156..60d11f335bc1 100644 --- a/pkg/core/resources/apis/system/secret.go +++ b/pkg/core/resources/apis/system/secret.go @@ -3,8 +3,6 @@ package system import ( "errors" - "github.com/Kong/kuma/pkg/core/resources/registry" - "github.com/Kong/kuma/pkg/core/resources/model" "github.com/gogo/protobuf/types" ) @@ -69,8 +67,3 @@ func (l *SecretResourceList) AddItem(r model.Resource) error { return model.ErrorInvalidItemType((*SecretResource)(nil), r) } } - -func init() { - registry.RegisterType(&SecretResource{}) - registry.RegistryListType(&SecretResourceList{}) -} diff --git a/pkg/core/resources/manager/customizable_manager.go b/pkg/core/resources/manager/customizable_manager.go index 74227f34647f..1ffd0918c3d0 100644 --- a/pkg/core/resources/manager/customizable_manager.go +++ b/pkg/core/resources/manager/customizable_manager.go @@ -35,6 +35,10 @@ func (m *customizableResourceManager) Delete(ctx context.Context, resource model return m.resourceManager(resource.GetType()).Delete(ctx, resource, fs...) } +func (m *customizableResourceManager) DeleteAll(ctx context.Context, list model.ResourceList, fs ...store.DeleteAllOptionsFunc) error { + return m.resourceManager(list.GetItemType()).DeleteAll(ctx, list, fs...) +} + func (m *customizableResourceManager) Update(ctx context.Context, resource model.Resource, fs ...store.UpdateOptionsFunc) error { return m.resourceManager(resource.GetType()).Update(ctx, resource, fs...) } diff --git a/pkg/core/resources/manager/manager.go b/pkg/core/resources/manager/manager.go index 959f9456a6ba..ca4ea749f1ee 100644 --- a/pkg/core/resources/manager/manager.go +++ b/pkg/core/resources/manager/manager.go @@ -13,6 +13,7 @@ type ResourceManager interface { Create(context.Context, model.Resource, ...store.CreateOptionsFunc) error Update(context.Context, model.Resource, ...store.UpdateOptionsFunc) error Delete(context.Context, model.Resource, ...store.DeleteOptionsFunc) error + DeleteAll(context.Context, model.ResourceList, ...store.DeleteAllOptionsFunc) error Get(context.Context, model.Resource, ...store.GetOptionsFunc) error List(context.Context, model.ResourceList, ...store.ListOptionsFunc) error } @@ -62,6 +63,20 @@ func (r *resourcesManager) Delete(ctx context.Context, resource model.Resource, return r.Store.Delete(ctx, resource, fs...) } +func (r *resourcesManager) DeleteAll(ctx context.Context, list model.ResourceList, fs ...store.DeleteAllOptionsFunc) error { + opts := store.NewDeleteAllOptions(fs...) + + if err := r.List(ctx, list, store.ListByMesh(opts.Mesh)); err != nil { + return err + } + for _, obj := range list.GetItems() { + if err := r.Delete(ctx, obj, store.DeleteBy(model.MetaToResourceKey(obj.GetMeta()))); err != nil && !store.IsResourceNotFound(err) { + return err + } + } + return nil +} + func (r *resourcesManager) Update(ctx context.Context, resource model.Resource, fs ...store.UpdateOptionsFunc) error { return r.Store.Update(ctx, resource, fs...) } diff --git a/pkg/core/resources/manager/manager_test.go b/pkg/core/resources/manager/manager_test.go index ef28c2cb9fa4..edfceb0943d4 100644 --- a/pkg/core/resources/manager/manager_test.go +++ b/pkg/core/resources/manager/manager_test.go @@ -5,6 +5,7 @@ import ( mesh_proto "github.com/Kong/kuma/api/mesh/v1alpha1" "github.com/Kong/kuma/pkg/core/resources/apis/mesh" "github.com/Kong/kuma/pkg/core/resources/manager" + "github.com/Kong/kuma/pkg/core/resources/model" "github.com/Kong/kuma/pkg/core/resources/store" "github.com/Kong/kuma/pkg/plugins/resources/memory" "github.com/Kong/kuma/pkg/test/apis/sample/v1alpha1" @@ -23,31 +24,31 @@ var _ = Describe("Resource Manager", func() { resManager = manager.NewResourceManager(resStore) }) - createSampleMesh := func() error { + createSampleMesh := func(name string) error { meshRes := mesh.MeshResource{ Spec: mesh_proto.Mesh{}, } - return resManager.Create(context.Background(), &meshRes, store.CreateByKey("default", "mesh-1", "mesh-1")) + return resManager.Create(context.Background(), &meshRes, store.CreateByKey("default", name, name)) } - createSampleResource := func() (*sample.TrafficRouteResource, error) { + createSampleResource := func(mesh string) (*sample.TrafficRouteResource, error) { trRes := sample.TrafficRouteResource{ Spec: v1alpha1.TrafficRoute{ Path: "/some", }, } - err := resManager.Create(context.Background(), &trRes, store.CreateByKey("default", "tr-1", "mesh-1")) + err := resManager.Create(context.Background(), &trRes, store.CreateByKey("default", "tr-1", mesh)) return &trRes, err } Describe("Create()", func() { It("should let create when mesh exists", func() { // given - err := createSampleMesh() + err := createSampleMesh("mesh-1") Expect(err).ToNot(HaveOccurred()) // when - _, err = createSampleResource() + _, err = createSampleResource("mesh-1") // then Expect(err).ToNot(HaveOccurred()) @@ -57,10 +58,50 @@ var _ = Describe("Resource Manager", func() { // given no mesh for resource // when - _, err := createSampleResource() + _, err := createSampleResource("mesh-1") // then Expect(err.Error()).To(Equal("mesh of name mesh-1 is not found")) }) }) + + Describe("DeleteAll()", func() { + It("should delete all resources within a mesh", func() { + // setup + Expect(createSampleMesh("mesh-1")).To(Succeed()) + Expect(createSampleMesh("mesh-2")).To(Succeed()) + _, err := createSampleResource("mesh-1") + Expect(err).ToNot(HaveOccurred()) + _, err = createSampleResource("mesh-2") + Expect(err).ToNot(HaveOccurred()) + + tlKey := model.ResourceKey{ + Mesh: "mesh-1", + Namespace: "default", + Name: "tl-1", + } + err = resManager.Create(context.Background(), &mesh.TrafficLogResource{}, store.CreateBy(tlKey)) + Expect(err).ToNot(HaveOccurred()) + + // when + err = resManager.DeleteAll(context.Background(), &sample.TrafficRouteResourceList{}, store.DeleteAllByMesh("mesh-1")) + + // then + Expect(err).ToNot(HaveOccurred()) + + // and resource from mesh-1 is deleted + res1 := sample.TrafficRouteResource{} + err = resManager.Get(context.Background(), &res1, store.GetByKey("default", "tr-1", "mesh-1")) + Expect(store.IsResourceNotFound(err)).To(BeTrue()) + + // and only TrafficRoutes are deleted + Expect(resManager.Get(context.Background(), &mesh.TrafficLogResource{}, store.GetBy(tlKey))).To(Succeed()) + + // and resource from mesh-2 is retained + res2 := sample.TrafficRouteResource{} + err = resManager.Get(context.Background(), &res2, store.GetByKey("default", "tr-1", "mesh-2")) + Expect(err).ToNot(HaveOccurred()) + + }) + }) }) diff --git a/pkg/core/resources/registry/registry.go b/pkg/core/resources/registry/registry.go index 4ebdd419249f..756d38e2cecf 100644 --- a/pkg/core/resources/registry/registry.go +++ b/pkg/core/resources/registry/registry.go @@ -12,6 +12,9 @@ type TypeRegistry interface { NewObject(model.ResourceType) (model.Resource, error) NewList(model.ResourceType) (model.ResourceList, error) + + ObjectTypes() []model.ResourceType + ListTypes() []model.ResourceType } func NewTypeRegistry() TypeRegistry { @@ -26,6 +29,22 @@ type typeRegistry struct { objectListTypes map[model.ResourceType]reflect.Type } +func (t *typeRegistry) ObjectTypes() []model.ResourceType { + var types []model.ResourceType + for typ := range t.objectTypes { + types = append(types, typ) + } + return types +} + +func (t *typeRegistry) ListTypes() []model.ResourceType { + var types []model.ResourceType + for typ := range t.objectListTypes { + types = append(types, typ) + } + return types +} + func (t *typeRegistry) RegisterType(res model.Resource) error { newType := reflect.TypeOf(res).Elem() if previous, ok := t.objectTypes[res.GetType()]; ok { diff --git a/pkg/core/resources/store/options.go b/pkg/core/resources/store/options.go index 388d9847d688..46c5fd2ae646 100644 --- a/pkg/core/resources/store/options.go +++ b/pkg/core/resources/store/options.go @@ -73,6 +73,26 @@ func DeleteByKey(ns, name, mesh string) DeleteOptionsFunc { } } +type DeleteAllOptions struct { + Mesh string +} + +type DeleteAllOptionsFunc func(*DeleteAllOptions) + +func DeleteAllByMesh(mesh string) DeleteAllOptionsFunc { + return func(opts *DeleteAllOptions) { + opts.Mesh = mesh + } +} + +func NewDeleteAllOptions(fs ...DeleteAllOptionsFunc) *DeleteAllOptions { + opts := &DeleteAllOptions{} + for _, f := range fs { + f(opts) + } + return opts +} + type GetOptions struct { Namespace string Name string diff --git a/pkg/core/secrets/manager/manager.go b/pkg/core/secrets/manager/manager.go index bc74ecc08eb2..654f28d83eb2 100644 --- a/pkg/core/secrets/manager/manager.go +++ b/pkg/core/secrets/manager/manager.go @@ -2,6 +2,7 @@ package manager import ( "context" + "github.com/Kong/kuma/pkg/core/resources/model" secret_model "github.com/Kong/kuma/pkg/core/resources/apis/system" core_store "github.com/Kong/kuma/pkg/core/resources/store" @@ -13,6 +14,7 @@ type SecretManager interface { Create(context.Context, *secret_model.SecretResource, ...core_store.CreateOptionsFunc) error Update(context.Context, *secret_model.SecretResource, ...core_store.UpdateOptionsFunc) error Delete(context.Context, *secret_model.SecretResource, ...core_store.DeleteOptionsFunc) error + DeleteAll(context.Context, ...core_store.DeleteAllOptionsFunc) error Get(context.Context, *secret_model.SecretResource, ...core_store.GetOptionsFunc) error List(context.Context, *secret_model.SecretResourceList, ...core_store.ListOptionsFunc) error } @@ -68,6 +70,20 @@ func (s *secretManager) Delete(ctx context.Context, secret *secret_model.SecretR return s.secretStore.Delete(ctx, secret, fs...) } +func (s *secretManager) DeleteAll(ctx context.Context, fs ...core_store.DeleteAllOptionsFunc) error { + list := &secret_model.SecretResourceList{} + opts := core_store.NewDeleteAllOptions(fs...) + if err := s.secretStore.List(context.Background(), list, core_store.ListByMesh(opts.Mesh)); err != nil { + return err + } + for _, item := range list.Items { + if err := s.Delete(ctx, item, core_store.DeleteBy(model.MetaToResourceKey(item.Meta))); err != nil && !core_store.IsResourceNotFound(err) { + return err + } + } + return nil +} + func (s *secretManager) encrypt(secret *secret_model.SecretResource) error { if len(secret.Spec.Value) > 0 { value, err := s.cipher.Encrypt(secret.Spec.Value) diff --git a/pkg/plugins/resources/k8s/store.go b/pkg/plugins/resources/k8s/store.go index fb2ba3057268..514581796b95 100644 --- a/pkg/plugins/resources/k8s/store.go +++ b/pkg/plugins/resources/k8s/store.go @@ -53,7 +53,7 @@ func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs ...store.UpdateOptionsFunc) error { obj, err := s.Converter.ToKubernetesObject(r) if err != nil { - return errors.Wrap(err, "failed to convert core model into k8s counterpart") + return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType()) } if err := s.Client.Update(ctx, obj); err != nil { if kube_apierrs.IsConflict(err) { @@ -81,7 +81,7 @@ func (s *KubernetesStore) Delete(ctx context.Context, r core_model.Resource, fs obj, err := s.Converter.ToKubernetesObject(r) if err != nil { - return errors.Wrap(err, "failed to convert core model into k8s counterpart") + return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType()) } obj.GetObjectMeta().SetNamespace(opts.Namespace) obj.GetObjectMeta().SetName(opts.Name) @@ -97,7 +97,7 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ... opts := store.NewGetOptions(fs...) obj, err := s.Converter.ToKubernetesObject(r) if err != nil { - return errors.Wrap(err, "failed to convert core model into k8s counterpart") + return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType()) } if err := s.Client.Get(ctx, kube_client.ObjectKey{Namespace: opts.Namespace, Name: opts.Name}, obj); err != nil { if kube_apierrs.IsNotFound(err) { @@ -117,7 +117,7 @@ func (s *KubernetesStore) List(ctx context.Context, rs core_model.ResourceList, opts := store.NewListOptions(fs...) obj, err := s.Converter.ToKubernetesList(rs) if err != nil { - return errors.Wrap(err, "failed to convert core model into k8s counterpart") + return errors.Wrapf(err, "failed to convert core list model of type %s into k8s counterpart", rs.GetItemType()) } if err := s.Client.List(ctx, obj, kube_client.InNamespace(opts.Namespace)); err != nil { return errors.Wrap(err, "failed to list k8s resources") diff --git a/pkg/plugins/resources/k8s/store_template_test.go b/pkg/plugins/resources/k8s/store_template_test.go index 72e02a448b9c..210b64785ef5 100644 --- a/pkg/plugins/resources/k8s/store_template_test.go +++ b/pkg/plugins/resources/k8s/store_template_test.go @@ -9,11 +9,12 @@ import ( k8s_registry "github.com/Kong/kuma/pkg/plugins/resources/k8s/native/pkg/registry" sample_k8s "github.com/Kong/kuma/pkg/plugins/resources/k8s/native/test/api/sample/v1alpha1" sample_proto "github.com/Kong/kuma/pkg/test/apis/sample/v1alpha1" + test_store "github.com/Kong/kuma/pkg/test/store" ) var _ = Describe("KubernetesStore template", func() { - store.ExecuteStoreTests(func() store.ResourceStore { + test_store.ExecuteStoreTests(func() store.ResourceStore { kubeTypes := k8s_registry.NewTypeRegistry() Expect(kubeTypes.RegisterObjectType(&sample_proto.TrafficRoute{}, &sample_k8s.TrafficRoute{})).To(Succeed()) Expect(kubeTypes.RegisterListType(&sample_proto.TrafficRoute{}, &sample_k8s.TrafficRouteList{})).To(Succeed()) diff --git a/pkg/plugins/resources/memory/store_test.go b/pkg/plugins/resources/memory/store_test.go index 3d7a0728a0ad..a27ba89990ea 100644 --- a/pkg/plugins/resources/memory/store_test.go +++ b/pkg/plugins/resources/memory/store_test.go @@ -3,6 +3,7 @@ package memory_test import ( "github.com/Kong/kuma/pkg/core/resources/store" "github.com/Kong/kuma/pkg/plugins/resources/memory" + test_store "github.com/Kong/kuma/pkg/test/store" . "github.com/onsi/ginkgo" ) @@ -11,5 +12,5 @@ var _ = Describe("MemoryStore", func() { return memory.NewStore() } - store.ExecuteStoreTests(createStore) + test_store.ExecuteStoreTests(createStore) }) diff --git a/pkg/plugins/resources/postgres/store_test.go b/pkg/plugins/resources/postgres/store_test.go index a1908c17c60e..8f05f7326c61 100644 --- a/pkg/plugins/resources/postgres/store_test.go +++ b/pkg/plugins/resources/postgres/store_test.go @@ -7,6 +7,7 @@ import ( "github.com/Kong/kuma/pkg/config" "github.com/Kong/kuma/pkg/config/plugins/resources/postgres" "github.com/Kong/kuma/pkg/core/resources/store" + test_store "github.com/Kong/kuma/pkg/test/store" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "math/rand" @@ -32,7 +33,7 @@ var _ = Describe("postgresResourceStore", func() { return pStore } - store.ExecuteStoreTests(createStore) + test_store.ExecuteStoreTests(createStore) }) func createRandomDb(cfg postgres.PostgresStoreConfig) (string, error) { diff --git a/pkg/plugins/runtime/k8s/controllers/mesh_controller.go b/pkg/plugins/runtime/k8s/controllers/mesh_controller.go index 8bdec7388af6..4e790d9e0c46 100644 --- a/pkg/plugins/runtime/k8s/controllers/mesh_controller.go +++ b/pkg/plugins/runtime/k8s/controllers/mesh_controller.go @@ -2,6 +2,8 @@ package controllers import ( "context" + "github.com/Kong/kuma/pkg/core/resources/manager" + "github.com/Kong/kuma/pkg/core/resources/store" "github.com/go-logr/logr" "github.com/pkg/errors" @@ -30,6 +32,7 @@ type MeshReconciler struct { Converter k8s_resources.Converter BuiltinCaManager builtin_ca.BuiltinCaManager SystemNamespace string + ResourceManager manager.ResourceManager } func (r *MeshReconciler) Reconcile(req kube_ctrl.Request) (kube_ctrl.Result, error) { @@ -40,7 +43,8 @@ func (r *MeshReconciler) Reconcile(req kube_ctrl.Request) (kube_ctrl.Result, err mesh := &mesh_k8s.Mesh{} if err := r.Get(ctx, req.NamespacedName, mesh); err != nil { if kube_apierrs.IsNotFound(err) { - return kube_ctrl.Result{}, nil + err := r.ResourceManager.Delete(ctx, &mesh_core.MeshResource{}, store.DeleteByKey(req.Namespace, req.Name, req.Name)) + return kube_ctrl.Result{}, err } log.Error(err, "unable to fetch Mesh") return kube_ctrl.Result{}, err diff --git a/pkg/plugins/runtime/k8s/plugin.go b/pkg/plugins/runtime/k8s/plugin.go index 3961b74debcf..4238244a2a97 100644 --- a/pkg/plugins/runtime/k8s/plugin.go +++ b/pkg/plugins/runtime/k8s/plugin.go @@ -73,6 +73,7 @@ func addMeshReconciler(mgr kube_ctrl.Manager, rt core_runtime.Runtime) error { Converter: k8s_resources.DefaultConverter(), BuiltinCaManager: rt.BuiltinCaManager(), SystemNamespace: rt.Config().Store.Kubernetes.SystemNamespace, + ResourceManager: rt.ResourceManager(), } return reconciler.SetupWithManager(mgr) } diff --git a/pkg/test/resources/apis/sample/sample_types.go b/pkg/test/resources/apis/sample/sample_types.go index 3f825279382a..84d736e8575a 100644 --- a/pkg/test/resources/apis/sample/sample_types.go +++ b/pkg/test/resources/apis/sample/sample_types.go @@ -3,6 +3,7 @@ package sample import ( "errors" "github.com/Kong/kuma/pkg/core/resources/model" + "github.com/Kong/kuma/pkg/core/resources/registry" proto "github.com/Kong/kuma/pkg/test/apis/sample/v1alpha1" ) @@ -67,3 +68,8 @@ func (l *TrafficRouteResourceList) AddItem(r model.Resource) error { return model.ErrorInvalidItemType((*TrafficRouteResource)(nil), r) } } + +func init() { + registry.RegisterType(&TrafficRouteResource{}) + registry.RegistryListType(&TrafficRouteResourceList{}) +} diff --git a/pkg/test/resources/registry.go b/pkg/test/resources/registry.go new file mode 100644 index 000000000000..e41b0adc2f9e --- /dev/null +++ b/pkg/test/resources/registry.go @@ -0,0 +1,11 @@ +package resources + +import ( + _ "github.com/Kong/kuma/pkg/core/resources/apis/mesh" // import to register all types + "github.com/Kong/kuma/pkg/core/resources/registry" + _ "github.com/Kong/kuma/pkg/test/resources/apis/sample" // import to register all types +) + +func Global() registry.TypeRegistry { + return registry.Global() +} diff --git a/pkg/test/runtime/runtime.go b/pkg/test/runtime/runtime.go index 1bde7bd510a4..8cdf24dabf20 100644 --- a/pkg/test/runtime/runtime.go +++ b/pkg/test/runtime/runtime.go @@ -6,6 +6,7 @@ import ( core_mesh "github.com/Kong/kuma/pkg/core/resources/apis/mesh" core_manager "github.com/Kong/kuma/pkg/core/resources/manager" core_model "github.com/Kong/kuma/pkg/core/resources/model" + "github.com/Kong/kuma/pkg/core/resources/registry" core_runtime "github.com/Kong/kuma/pkg/core/runtime" secret_cipher "github.com/Kong/kuma/pkg/core/secrets/cipher" secret_manager "github.com/Kong/kuma/pkg/core/secrets/manager" @@ -53,10 +54,9 @@ func newBuiltinCaManager(builder *core_runtime.Builder) builtin_ca.BuiltinCaMana func newResourceManager(builder *core_runtime.Builder) core_manager.ResourceManager { defaultManager := core_manager.NewResourceManager(builder.ResourceStore()) - meshManager := mesh_managers.NewMeshManager(builder.ResourceStore(), builder.BuiltinCaManager()) - customManagers := map[core_model.ResourceType]core_manager.ResourceManager{ - core_mesh.MeshType: meshManager, - } + customManagers := map[core_model.ResourceType]core_manager.ResourceManager{} customizableManager := core_manager.NewCustomizableResourceManager(defaultManager, customManagers) + meshManager := mesh_managers.NewMeshManager(builder.ResourceStore(), builder.BuiltinCaManager(), customizableManager, builder.SecretManager(), registry.Global()) + customManagers[core_mesh.MeshType] = meshManager return customizableManager } diff --git a/pkg/core/resources/store/store_test_template.go b/pkg/test/store/store_test_template.go similarity index 74% rename from pkg/core/resources/store/store_test_template.go rename to pkg/test/store/store_test_template.go index 45abd181f5df..2d24028adae1 100644 --- a/pkg/core/resources/store/store_test_template.go +++ b/pkg/test/store/store_test_template.go @@ -2,6 +2,7 @@ package store import ( "context" + "github.com/Kong/kuma/pkg/core/resources/store" sample_proto "github.com/Kong/kuma/pkg/test/apis/sample/v1alpha1" sample_model "github.com/Kong/kuma/pkg/test/resources/apis/sample" . "github.com/onsi/ginkgo" @@ -10,15 +11,15 @@ import ( ) func ExecuteStoreTests( - createStore func() ResourceStore, + createStore func() store.ResourceStore, ) { var namespace string const mesh = "default-mesh" - var s ClosableResourceStore + var s store.ClosableResourceStore BeforeEach(func() { namespace = string(uuid.New()) - s = NewStrictResourceStore(createStore()) + s = store.NewStrictResourceStore(createStore()) }) AfterEach(func() { @@ -32,7 +33,7 @@ func ExecuteStoreTests( Path: "demo", }, } - err := s.Create(context.Background(), &res, CreateByKey(namespace, name, mesh)) + err := s.Create(context.Background(), &res, store.CreateByKey(namespace, name, mesh)) Expect(err).ToNot(HaveOccurred()) return &res } @@ -47,7 +48,7 @@ func ExecuteStoreTests( // when retrieve created object resource := sample_model.TrafficRouteResource{} - err := s.Get(context.Background(), &resource, GetByKey(namespace, name, mesh)) + err := s.Get(context.Background(), &resource, store.GetByKey(namespace, name, mesh)) // then Expect(err).ToNot(HaveOccurred()) @@ -67,10 +68,10 @@ func ExecuteStoreTests( // when try to create another one with same name resource.SetMeta(nil) - err := s.Create(context.Background(), resource, CreateByKey(namespace, name, mesh)) + err := s.Create(context.Background(), resource, store.CreateByKey(namespace, name, mesh)) // then - Expect(err).To(MatchError(ErrorResourceAlreadyExists(resource.GetType(), namespace, name, mesh))) + Expect(err).To(MatchError(store.ErrorResourceAlreadyExists(resource.GetType(), namespace, name, mesh))) }) }) @@ -84,7 +85,7 @@ func ExecuteStoreTests( err := s.Delete( context.Background(), resource, - DeleteByKey(resource.GetMeta().GetNamespace(), resource.Meta.GetName(), mesh), + store.DeleteByKey(resource.GetMeta().GetNamespace(), resource.Meta.GetName(), mesh), ) // then @@ -94,7 +95,7 @@ func ExecuteStoreTests( err = s.Update(context.Background(), resource) // then - Expect(err).To(MatchError(ErrorResourceConflict(resource.GetType(), namespace, name, mesh))) + Expect(err).To(MatchError(store.ErrorResourceConflict(resource.GetType(), namespace, name, mesh))) }) It("should update an existing resource", func() { @@ -111,7 +112,7 @@ func ExecuteStoreTests( // when retrieve the resource res := sample_model.TrafficRouteResource{} - err = s.Get(context.Background(), &res, GetByKey(namespace, name, mesh)) + err = s.Get(context.Background(), &res, store.GetByKey(namespace, name, mesh)) // then Expect(err).ToNot(HaveOccurred()) @@ -129,7 +130,7 @@ func ExecuteStoreTests( resource := sample_model.TrafficRouteResource{} // when - err := s.Delete(context.TODO(), &resource, DeleteByKey(namespace, "non-existent-name", mesh)) + err := s.Delete(context.TODO(), &resource, store.DeleteByKey(namespace, "non-existent-name", mesh)) // then Expect(err).ToNot(HaveOccurred()) @@ -142,14 +143,14 @@ func ExecuteStoreTests( // when resource.SetMeta(nil) // otherwise the validation from strict client fires that mesh is different - err := s.Delete(context.TODO(), resource, DeleteByKey(namespace, name, "different-mesh")) + err := s.Delete(context.TODO(), resource, store.DeleteByKey(namespace, name, "different-mesh")) // then Expect(err).ToNot(HaveOccurred()) // and when getting the given resource getResource := sample_model.TrafficRouteResource{} - err = s.Get(context.Background(), &getResource, GetByKey(namespace, name, mesh)) + err = s.Get(context.Background(), &getResource, store.GetByKey(namespace, name, mesh)) // then resource still exists Expect(err).ToNot(HaveOccurred()) @@ -162,17 +163,17 @@ func ExecuteStoreTests( // when resource := sample_model.TrafficRouteResource{} - err := s.Delete(context.TODO(), &resource, DeleteByKey(namespace, name, mesh)) + err := s.Delete(context.TODO(), &resource, store.DeleteByKey(namespace, name, mesh)) // then Expect(err).ToNot(HaveOccurred()) // when query for deleted resource resource = sample_model.TrafficRouteResource{} - err = s.Get(context.Background(), &resource, GetByKey(namespace, name, mesh)) + err = s.Get(context.Background(), &resource, store.GetByKey(namespace, name, mesh)) // then resource cannot be found - Expect(err).To(Equal(ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) + Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) }) }) @@ -183,10 +184,10 @@ func ExecuteStoreTests( resource := sample_model.TrafficRouteResource{} // when - err := s.Get(context.Background(), &resource, GetByKey(namespace, name, mesh)) + err := s.Get(context.Background(), &resource, store.GetByKey(namespace, name, mesh)) // then - Expect(err).To(MatchError(ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) + Expect(err).To(MatchError(store.ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) }) It("should return an error if resource is not found in given mesh", func() { @@ -197,10 +198,10 @@ func ExecuteStoreTests( // when resource := sample_model.TrafficRouteResource{} - err := s.Get(context.Background(), &resource, GetByKey(namespace, name, mesh)) + err := s.Get(context.Background(), &resource, store.GetByKey(namespace, name, mesh)) // then - Expect(err).To(Equal(ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) + Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), namespace, name, mesh))) }) It("should return an existing resource", func() { @@ -210,7 +211,7 @@ func ExecuteStoreTests( // when res := sample_model.TrafficRouteResource{} - err := s.Get(context.Background(), &res, GetByKey(namespace, name, mesh)) + err := s.Get(context.Background(), &res, store.GetByKey(namespace, name, mesh)) // then Expect(err).ToNot(HaveOccurred()) @@ -229,7 +230,7 @@ func ExecuteStoreTests( list := sample_model.TrafficRouteResourceList{} // when - err := s.List(context.Background(), &list, ListByNamespace("non-existent-namespace"), ListByMesh(mesh)) + err := s.List(context.Background(), &list, store.ListByNamespace("non-existent-namespace"), store.ListByMesh(mesh)) // then Expect(err).ToNot(HaveOccurred()) @@ -245,7 +246,7 @@ func ExecuteStoreTests( list := sample_model.TrafficRouteResourceList{} // when - err := s.List(context.Background(), &list, ListByNamespace(namespace)) + err := s.List(context.Background(), &list, store.ListByNamespace(namespace)) // then Expect(err).ToNot(HaveOccurred()) @@ -270,7 +271,7 @@ func ExecuteStoreTests( list := sample_model.TrafficRouteResourceList{} // when - err := s.List(context.Background(), &list, ListByNamespace("different-namespace")) + err := s.List(context.Background(), &list, store.ListByNamespace("different-namespace")) // then Expect(err).ToNot(HaveOccurred()) @@ -286,7 +287,7 @@ func ExecuteStoreTests( list := sample_model.TrafficRouteResourceList{} // when - err := s.List(context.Background(), &list, ListByMesh("different-mesh")) + err := s.List(context.Background(), &list, store.ListByMesh("different-mesh")) // then Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/xds/server/dataplane_status_sink_test.go b/pkg/xds/server/dataplane_status_sink_test.go index 60591a31270a..4bc9d1b563ca 100644 --- a/pkg/xds/server/dataplane_status_sink_test.go +++ b/pkg/xds/server/dataplane_status_sink_test.go @@ -2,6 +2,7 @@ package server import ( "context" + "github.com/Kong/kuma/pkg/core/resources/manager" "time" . "github.com/onsi/ginkgo" @@ -132,6 +133,8 @@ var _ = Describe("DataplaneInsightSink", func() { BeforeEach(func() { store = memory_resources.NewStore() + err := store.Create(context.Background(), &mesh_core.MeshResource{}, core_store.CreateByKey("default", "default", "default")) + Expect(err).ToNot(HaveOccurred()) }) It("should create/update DataplaneInsight resource", func() { @@ -146,7 +149,7 @@ var _ = Describe("DataplaneInsightSink", func() { lastSeenVersion := "" // given - statusStore := NewDataplaneInsightStore(store) + statusStore := NewDataplaneInsightStore(manager.NewResourceManager(store)) // when err := statusStore.Upsert(key, proto.Clone(subscription).(*mesh_proto.DiscoverySubscription))