diff --git a/broker/volumebroker/server/event_list.go b/broker/volumebroker/server/event_list.go new file mode 100644 index 000000000..a6d8102e0 --- /dev/null +++ b/broker/volumebroker/server/event_list.go @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + "github.com/ironcore-dev/ironcore/broker/volumebroker/apiutils" + irievent "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" +) + +const ( + InvolvedObjectKind = "Volume" + InvolvedObjectKindSelector = "involvedObject.kind" + InvolvedObjectAPIVersionSelector = "involvedObject.apiVersion" +) + +func (s *Server) listEvents(ctx context.Context) ([]*irievent.Event, error) { + log := ctrl.LoggerFrom(ctx) + volumeEventList := &v1.EventList{} + selectorField := fields.Set{ + InvolvedObjectKindSelector: InvolvedObjectKind, + InvolvedObjectAPIVersionSelector: storagev1alpha1.SchemeGroupVersion.String(), + } + if err := s.client.List(ctx, volumeEventList, + client.InNamespace(s.namespace), client.MatchingFieldsSelector{Selector: selectorField.AsSelector()}, + ); err != nil { + return nil, err + } + + var iriEvents []*irievent.Event + for _, volumeEvent := range volumeEventList.Items { + ironcoreVolume, err := s.getIronCoreVolume(ctx, volumeEvent.InvolvedObject.Name) + if err != nil { + log.V(1).Info("Unable to get ironcore volume", "VolumeName", volumeEvent.InvolvedObject.Name) + continue + } + volumeObjectMetadata, err := apiutils.GetObjectMetadata(&ironcoreVolume.ObjectMeta) + if err != nil { + continue + } + iriEvent := &irievent.Event{ + Spec: &irievent.EventSpec{ + InvolvedObjectMeta: volumeObjectMetadata, + Reason: volumeEvent.Reason, + Message: volumeEvent.Message, + Type: volumeEvent.Type, + EventTime: volumeEvent.LastTimestamp.Unix(), + }, + } + iriEvents = append(iriEvents, iriEvent) + } + return iriEvents, nil +} + +func (s *Server) filterEvents(events []*irievent.Event, filter *iri.EventFilter) []*irievent.Event { + if filter == nil { + return events + } + + var ( + res []*irievent.Event + sel = labels.SelectorFromSet(filter.LabelSelector) + ) + for _, iriEvent := range events { + if !sel.Matches(labels.Set(iriEvent.Spec.InvolvedObjectMeta.Labels)) { + continue + } + + if filter.EventsFromTime > 0 && filter.EventsToTime > 0 { + if iriEvent.Spec.EventTime < filter.EventsFromTime || iriEvent.Spec.EventTime > filter.EventsToTime { + continue + } + } + + res = append(res, iriEvent) + } + return res +} + +func (s *Server) ListEvents(ctx context.Context, req *iri.ListEventsRequest) (*iri.ListEventsResponse, error) { + iriEvents, err := s.listEvents(ctx) + if err != nil { + return nil, fmt.Errorf("error listing volume events : %w", err) + } + + iriEvents = s.filterEvents(iriEvents, req.Filter) + + return &iri.ListEventsResponse{ + Events: iriEvents, + }, nil +} diff --git a/broker/volumebroker/server/event_list_test.go b/broker/volumebroker/server/event_list_test.go new file mode 100644 index 000000000..fb93cd3db --- /dev/null +++ b/broker/volumebroker/server/event_list_test.go @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + "time" + + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + irievent "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + + irimeta "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("ListEvents", func() { + ns, srv := SetupTest() + volumeClass := SetupVolumeClass() + + It("should correctly list events", func(ctx SpecContext) { + By("creating volume") + Expect(storagev1alpha1.AddToScheme(scheme.Scheme)).To(Succeed()) + + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + res, err := srv.CreateVolume(ctx, &iri.CreateVolumeRequest{ + Volume: &iri.Volume{ + Metadata: &irimeta.ObjectMetadata{ + Labels: map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }, + }, + Spec: &iri.VolumeSpec{ + Class: volumeClass.Name, + Resources: &iri.VolumeResources{ + StorageBytes: 100, + }, + }, + }, + }) + + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + + By("getting the ironcore volume") + ironcoreVolume := &storagev1alpha1.Volume{} + ironcoreVolumeKey := client.ObjectKey{Namespace: ns.Name, Name: res.Volume.Metadata.Id} + Expect(k8sClient.Get(ctx, ironcoreVolumeKey, ironcoreVolume)).To(Succeed()) + + By("generating the volume events") + eventGeneratedTime := time.Now() + eventRecorder := k8sManager.GetEventRecorderFor("test-recorder") + eventRecorder.Event(ironcoreVolume, corev1.EventTypeNormal, "testing", "this is test event") + + By("listing the volume events with no filters") + Eventually(func(g Gomega) []*irievent.Event { + resp, err := srv.ListEvents(ctx, &iri.ListEventsRequest{}) + g.Expect(err).NotTo(HaveOccurred()) + return resp.Events + }).Should(ConsistOf( + HaveField("Spec", SatisfyAll( + HaveField("InvolvedObjectMeta.Id", Equal(ironcoreVolume.Name)), + HaveField("Reason", Equal("testing")), + HaveField("Message", Equal("this is test event")), + HaveField("Type", Equal(corev1.EventTypeNormal)), + )), + ), + ) + + By("listing the volume events with matching label and time filters") + resp, err := srv.ListEvents(ctx, &iri.ListEventsRequest{Filter: &iri.EventFilter{ + LabelSelector: map[string]string{volumepoolletv1alpha1.VolumeUIDLabel: "foobar"}, + EventsFromTime: eventGeneratedTime.Unix(), + EventsToTime: time.Now().Unix(), + }}) + + Expect(err).NotTo(HaveOccurred()) + + Expect(resp.Events).To(ConsistOf( + HaveField("Spec", SatisfyAll( + HaveField("InvolvedObjectMeta.Id", Equal(ironcoreVolume.Name)), + HaveField("Reason", Equal("testing")), + HaveField("Message", Equal("this is test event")), + HaveField("Type", Equal(corev1.EventTypeNormal)), + )), + ), + ) + + By("listing the volume events with non matching label filter") + resp, err = srv.ListEvents(ctx, &iri.ListEventsRequest{Filter: &iri.EventFilter{ + LabelSelector: map[string]string{"foo": "bar"}, + EventsFromTime: eventGeneratedTime.Unix(), + EventsToTime: time.Now().Unix(), + }}) + Expect(err).NotTo(HaveOccurred()) + + Expect(resp.Events).To(BeEmpty()) + + By("listing the volume events with matching label filter and non matching time filter") + resp, err = srv.ListEvents(ctx, &iri.ListEventsRequest{Filter: &iri.EventFilter{ + LabelSelector: map[string]string{volumepoolletv1alpha1.VolumeUIDLabel: "foobar"}, + EventsFromTime: eventGeneratedTime.Add(-10 * time.Minute).Unix(), + EventsToTime: eventGeneratedTime.Add(-5 * time.Minute).Unix(), + }}) + Expect(err).NotTo(HaveOccurred()) + + Expect(resp.Events).To(BeEmpty()) + }) +}) diff --git a/broker/volumebroker/server/server.go b/broker/volumebroker/server/server.go index 65c803f7a..acfd94322 100644 --- a/broker/volumebroker/server/server.go +++ b/broker/volumebroker/server/server.go @@ -88,6 +88,7 @@ func setOptionsDefaults(o *Options) { var _ iri.VolumeRuntimeServer = (*Server)(nil) +//+kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=storage.ironcore.dev,resources=volumes,verbs=get;list;watch;create;update;patch;delete diff --git a/broker/volumebroker/server/server_suite_test.go b/broker/volumebroker/server/server_suite_test.go new file mode 100644 index 000000000..d78cbac22 --- /dev/null +++ b/broker/volumebroker/server/server_suite_test.go @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + "context" + "testing" + "time" + + "github.com/ironcore-dev/controller-utils/buildutils" + "github.com/ironcore-dev/controller-utils/modutils" + corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1" + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + "github.com/ironcore-dev/ironcore/broker/common/idgen" + "github.com/ironcore-dev/ironcore/broker/volumebroker/server" + utilsenvtest "github.com/ironcore-dev/ironcore/utils/envtest" + "github.com/ironcore-dev/ironcore/utils/envtest/apiserver" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +var ( + cfg *rest.Config + testEnv *envtest.Environment + testEnvExt *utilsenvtest.EnvironmentExtensions + k8sClient client.Client +) + +const ( + eventuallyTimeout = 3 * time.Second + pollingInterval = 50 * time.Millisecond + consistentlyDuration = 1 * time.Second + apiServiceTimeout = 5 * time.Minute +) + +func TestServer(t *testing.T) { + SetDefaultConsistentlyPollingInterval(pollingInterval) + SetDefaultEventuallyPollingInterval(pollingInterval) + SetDefaultEventuallyTimeout(eventuallyTimeout) + SetDefaultConsistentlyDuration(consistentlyDuration) + + RegisterFailHandler(Fail) + RunSpecs(t, "Server Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + var err error + By("bootstrapping test environment") + testEnv = &envtest.Environment{} + testEnvExt = &utilsenvtest.EnvironmentExtensions{ + APIServiceDirectoryPaths: []string{ + modutils.Dir("github.com/ironcore-dev/ironcore", "config", "apiserver", "apiservice", "bases"), + }, + ErrorIfAPIServicePathIsMissing: true, + } + + cfg, err = utilsenvtest.StartWithExtensions(testEnv, testEnvExt) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + DeferCleanup(utilsenvtest.StopWithExtensions, testEnv, testEnvExt) + + Expect(storagev1alpha1.AddToScheme(scheme.Scheme)).To(Succeed()) + + // Init package-level k8sClient + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + SetClient(k8sClient) + + apiSrv, err := apiserver.New(cfg, apiserver.Options{ + MainPath: "github.com/ironcore-dev/ironcore/cmd/ironcore-apiserver", + BuildOptions: []buildutils.BuildOption{buildutils.ModModeMod}, + ETCDServers: []string{testEnv.ControlPlane.Etcd.URL.String()}, + Host: testEnvExt.APIServiceInstallOptions.LocalServingHost, + Port: testEnvExt.APIServiceInstallOptions.LocalServingPort, + CertDir: testEnvExt.APIServiceInstallOptions.LocalServingCertDir, + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(apiSrv.Start()).To(Succeed()) + DeferCleanup(apiSrv.Stop) + + Expect(utilsenvtest.WaitUntilAPIServicesReadyWithTimeout(apiServiceTimeout, testEnvExt, k8sClient, scheme.Scheme)).To(Succeed()) +}) + +func SetupTest() (*corev1.Namespace, *server.Server) { + var ( + ns = &corev1.Namespace{} + srv = &server.Server{} + volumePool = &storagev1alpha1.VolumePool{} + ) + + BeforeEach(func(ctx SpecContext) { + *ns = corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-ns-", + }, + } + Expect(k8sClient.Create(ctx, ns)).To(Succeed(), "failed to create test namespace") + DeferCleanup(k8sClient.Delete, ns) + + By("creating a volume pool") + *volumePool = storagev1alpha1.VolumePool{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "volumepool-", + Labels: map[string]string{ + "pool": "test-pool", + }, + }, + Spec: storagev1alpha1.VolumePoolSpec{ + ProviderID: "network-id", + }, + } + Expect(k8sClient.Create(ctx, volumePool)).To(Succeed(), "failed to create test volume pool") + DeferCleanup(k8sClient.Delete, volumePool) + + newSrv, err := server.New(cfg, server.Options{ + Namespace: ns.Name, + VolumePoolName: volumePool.Name, + VolumePoolSelector: map[string]string{ + "pool": "test-pool", + }, + IDGen: idgen.Default, + }) + Expect(err).NotTo(HaveOccurred()) + *srv = *newSrv + }) + + return ns, srv +} + +func SetupVolumeClass() *storagev1alpha1.VolumeClass { + volumeClass := &storagev1alpha1.VolumeClass{} + + BeforeEach(func(ctx SpecContext) { + *volumeClass = storagev1alpha1.VolumeClass{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "volume-class-", + }, + Capabilities: corev1alpha1.ResourceList{ + corev1alpha1.ResourceIOPS: resource.MustParse("250Mi"), + corev1alpha1.ResourceTPS: resource.MustParse("1500"), + }, + } + Expect(k8sClient.Create(ctx, volumeClass)).To(Succeed()) + DeferCleanup(func(ctx context.Context) error { + return client.IgnoreNotFound(k8sClient.Delete(ctx, volumeClass)) + }) + }) + + return volumeClass +} diff --git a/broker/volumebroker/server/volume_create_test.go b/broker/volumebroker/server/volume_create_test.go new file mode 100644 index 000000000..90e2aaf23 --- /dev/null +++ b/broker/volumebroker/server/volume_create_test.go @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + "github.com/ironcore-dev/ironcore/broker/machinebroker/apiutils" + volumebrokerv1alpha1 "github.com/ironcore-dev/ironcore/broker/volumebroker/api/v1alpha1" + irimeta "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var _ = Describe("CreateVolume", func() { + ns, srv := SetupTest() + volumeClass := SetupVolumeClass() + + It("should correctly create a volume", func(ctx SpecContext) { + By("creating a volume") + res, err := srv.CreateVolume(ctx, &iri.CreateVolumeRequest{ + Volume: &iri.Volume{ + Metadata: &irimeta.ObjectMetadata{ + Labels: map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }, + }, + Spec: &iri.VolumeSpec{ + Class: volumeClass.Name, + Resources: &iri.VolumeResources{ + StorageBytes: 100, + }, + }, + }, + }) + + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + + By("getting the ironcore volume") + ironcoreVolume := &storagev1alpha1.Volume{} + ironcoreVolumeKey := client.ObjectKey{Namespace: ns.Name, Name: res.Volume.Metadata.Id} + Expect(k8sClient.Get(ctx, ironcoreVolumeKey, ironcoreVolume)).To(Succeed()) + + By("inspecting the ironcore volume") + Expect(ironcoreVolume.Labels).To(Equal(map[string]string{ + volumebrokerv1alpha1.CreatedLabel: "true", + volumebrokerv1alpha1.ManagerLabel: volumebrokerv1alpha1.VolumeBrokerManager, + })) + encodedIRIAnnotations, err := apiutils.EncodeAnnotationsAnnotation(nil) + Expect(err).NotTo(HaveOccurred()) + encodedIRILabels, err := apiutils.EncodeLabelsAnnotation(map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }) + Expect(err).NotTo(HaveOccurred()) + Expect(ironcoreVolume.Annotations).To(Equal(map[string]string{ + volumebrokerv1alpha1.AnnotationsAnnotation: encodedIRIAnnotations, + volumebrokerv1alpha1.LabelsAnnotation: encodedIRILabels, + })) + Expect(ironcoreVolume.Spec.VolumeClassRef.Name).To(Equal(volumeClass.Name)) + Expect(ironcoreVolume.Spec.Resources).To(HaveLen(1)) + }) +}) diff --git a/broker/volumebroker/server/volume_delete_test.go b/broker/volumebroker/server/volume_delete_test.go new file mode 100644 index 000000000..bc7f6cb94 --- /dev/null +++ b/broker/volumebroker/server/volume_delete_test.go @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + irimeta "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("DeleteVolume", func() { + ns, srv := SetupTest() + volumeClass := SetupVolumeClass() + + It("should correctly delete a volume", func(ctx SpecContext) { + By("creating a volume") + createRes, err := srv.CreateVolume(ctx, &iri.CreateVolumeRequest{ + Volume: &iri.Volume{ + Metadata: &irimeta.ObjectMetadata{ + Labels: map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }, + }, + Spec: &iri.VolumeSpec{ + Class: volumeClass.Name, + Resources: &iri.VolumeResources{ + StorageBytes: 100, + }, + }, + }, + }) + + Expect(err).NotTo(HaveOccurred()) + Expect(createRes).NotTo(BeNil()) + + By("deleting the volume") + deleteRes, err := srv.DeleteVolume(ctx, &iri.DeleteVolumeRequest{ + VolumeId: createRes.Volume.Metadata.Id, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(deleteRes).NotTo(BeNil()) + + By("verifying the volume is deleted") + ironcoreVolume := &storagev1alpha1.Volume{} + ironcoreVolumeKey := client.ObjectKey{Namespace: ns.Name, Name: createRes.Volume.Metadata.Id} + err = k8sClient.Get(ctx, ironcoreVolumeKey, ironcoreVolume) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }) +}) diff --git a/broker/volumebroker/server/volume_expand_test.go b/broker/volumebroker/server/volume_expand_test.go new file mode 100644 index 000000000..f7ffc695d --- /dev/null +++ b/broker/volumebroker/server/volume_expand_test.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1" + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + irimeta "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("ExpandVolume", func() { + ns, srv := SetupTest() + + It("should correctly Expand a volume", func(ctx SpecContext) { + By("creating a volume class with resize policy expandonly") + volumeClass := &storagev1alpha1.VolumeClass{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "volume-class-", + }, + Capabilities: corev1alpha1.ResourceList{ + corev1alpha1.ResourceIOPS: resource.MustParse("250Mi"), + corev1alpha1.ResourceTPS: resource.MustParse("1500"), + }, + ResizePolicy: storagev1alpha1.ResizePolicyExpandOnly, + } + + Expect(k8sClient.Create(ctx, volumeClass)).To(Succeed(), "failed to create test volume class") + DeferCleanup(k8sClient.Delete, volumeClass) + + By("creating a volume") + createRes, err := srv.CreateVolume(ctx, &iri.CreateVolumeRequest{ + Volume: &iri.Volume{ + Metadata: &irimeta.ObjectMetadata{ + Labels: map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }, + }, + Spec: &iri.VolumeSpec{ + Class: volumeClass.Name, + Resources: &iri.VolumeResources{ + StorageBytes: 100, + }, + }, + }, + }) + + Expect(err).NotTo(HaveOccurred()) + Expect(createRes).NotTo(BeNil()) + + By("checking the volume storage size") + ironcoreVolume := &storagev1alpha1.Volume{} + ironcoreVolumeKey := client.ObjectKey{Namespace: ns.Name, Name: createRes.Volume.Metadata.Id} + Expect(k8sClient.Get(ctx, ironcoreVolumeKey, ironcoreVolume)).To(Succeed()) + + Expect(ironcoreVolume.Spec.Resources.Storage().String()).To(Equal("100")) + + By("expanding the volume size") + expandRes, err := srv.ExpandVolume(ctx, &iri.ExpandVolumeRequest{ + VolumeId: createRes.Volume.Metadata.Id, + Resources: &iri.VolumeResources{ + StorageBytes: 200, + }, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(expandRes).NotTo(BeNil()) + + By("verifying the volume storage is expanded") + Expect(k8sClient.Get(ctx, ironcoreVolumeKey, ironcoreVolume)).To(Succeed()) + Expect(ironcoreVolume.Spec.Resources.Storage().String()).To(Equal("200")) + + }) +}) diff --git a/broker/volumebroker/server/volume_list.go b/broker/volumebroker/server/volume_list.go index eae788eeb..640bba7b9 100644 --- a/broker/volumebroker/server/volume_list.go +++ b/broker/volumebroker/server/volume_list.go @@ -10,6 +10,7 @@ import ( storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" "github.com/ironcore-dev/ironcore/broker/common" volumebrokerv1alpha1 "github.com/ironcore-dev/ironcore/broker/volumebroker/api/v1alpha1" + "github.com/ironcore-dev/ironcore/broker/volumebroker/apiutils" iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -131,6 +132,21 @@ func (s *Server) aggregateIronCoreVolume( }, nil } +func (s *Server) getIronCoreVolume(ctx context.Context, id string) (*storagev1alpha1.Volume, error) { + ironcoreVolume := &storagev1alpha1.Volume{} + ironcoreVolumeKey := client.ObjectKey{Namespace: s.namespace, Name: id} + if err := s.client.Get(ctx, ironcoreVolumeKey, ironcoreVolume); err != nil { + if !apierrors.IsNotFound(err) { + return nil, fmt.Errorf("error getting ironcore volume %s: %w", id, err) + } + return nil, status.Errorf(codes.NotFound, "volume %s not found", id) + } + if !apiutils.IsManagedBy(ironcoreVolume, volumebrokerv1alpha1.VolumeBrokerManager) || !apiutils.IsCreated(ironcoreVolume) { + return nil, status.Errorf(codes.NotFound, "volume %s not found", id) + } + return ironcoreVolume, nil +} + func (s *Server) getAggregateIronCoreVolume(ctx context.Context, id string) (*AggregateIronCoreVolume, error) { ironcoreVolume := &storagev1alpha1.Volume{} if err := s.getManagedAndCreated(ctx, id, ironcoreVolume); err != nil { diff --git a/broker/volumebroker/server/volume_list_test.go b/broker/volumebroker/server/volume_list_test.go new file mode 100644 index 000000000..4a840e1c6 --- /dev/null +++ b/broker/volumebroker/server/volume_list_test.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package server_test + +import ( + irimeta "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("ListVolumes", func() { + _, srv := SetupTest() + volumeClass := SetupVolumeClass() + + It("should correctly list volumes", func(ctx SpecContext) { + By("creating multiple volumes") + const noOfVolumes = 3 + + Volumes := make([]any, noOfVolumes) + for i := 0; i < noOfVolumes; i++ { + res, err := srv.CreateVolume(ctx, &iri.CreateVolumeRequest{ + Volume: &iri.Volume{ + Metadata: &irimeta.ObjectMetadata{ + Labels: map[string]string{ + volumepoolletv1alpha1.VolumeUIDLabel: "foobar", + }, + }, + Spec: &iri.VolumeSpec{ + Class: volumeClass.Name, + Resources: &iri.VolumeResources{ + StorageBytes: 100, + }, + }, + }, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(res).NotTo(BeNil()) + Volumes[i] = res.Volume + } + + By("listing the Volumes") + Expect(srv.ListVolumes(ctx, &iri.ListVolumesRequest{})).To(HaveField("Volumes", ConsistOf(Volumes...))) + }) +}) diff --git a/config/volumepoollet-broker/broker-rbac/role.yaml b/config/volumepoollet-broker/broker-rbac/role.yaml index 4e7bbbe5c..26d55bec4 100644 --- a/config/volumepoollet-broker/broker-rbac/role.yaml +++ b/config/volumepoollet-broker/broker-rbac/role.yaml @@ -4,6 +4,14 @@ kind: Role metadata: name: broker-role rules: +- apiGroups: + - "" + resources: + - events + verbs: + - get + - list + - watch - apiGroups: - "" resources: diff --git a/iri/apis/volume/v1alpha1/api.pb.go b/iri/apis/volume/v1alpha1/api.pb.go index 3f475d9b9..f60a43145 100644 --- a/iri/apis/volume/v1alpha1/api.pb.go +++ b/iri/apis/volume/v1alpha1/api.pb.go @@ -15,6 +15,7 @@ import ( _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys" + v1alpha11 "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" v1alpha1 "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -113,6 +114,75 @@ func (m *VolumeFilter) GetLabelSelector() map[string]string { return nil } +type EventFilter struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + LabelSelector map[string]string `protobuf:"bytes,2,rep,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + EventsFromTime int64 `protobuf:"varint,3,opt,name=events_from_time,json=eventsFromTime,proto3" json:"events_from_time,omitempty"` + EventsToTime int64 `protobuf:"varint,4,opt,name=events_to_time,json=eventsToTime,proto3" json:"events_to_time,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EventFilter) Reset() { *m = EventFilter{} } +func (*EventFilter) ProtoMessage() {} +func (*EventFilter) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{1} +} +func (m *EventFilter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventFilter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventFilter.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventFilter) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventFilter.Merge(m, src) +} +func (m *EventFilter) XXX_Size() int { + return m.Size() +} +func (m *EventFilter) XXX_DiscardUnknown() { + xxx_messageInfo_EventFilter.DiscardUnknown(m) +} + +var xxx_messageInfo_EventFilter proto.InternalMessageInfo + +func (m *EventFilter) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *EventFilter) GetLabelSelector() map[string]string { + if m != nil { + return m.LabelSelector + } + return nil +} + +func (m *EventFilter) GetEventsFromTime() int64 { + if m != nil { + return m.EventsFromTime + } + return 0 +} + +func (m *EventFilter) GetEventsToTime() int64 { + if m != nil { + return m.EventsToTime + } + return 0 +} + type VolumeResources struct { StorageBytes int64 `protobuf:"varint,1,opt,name=storage_bytes,json=storageBytes,proto3" json:"storage_bytes,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -122,7 +192,7 @@ type VolumeResources struct { func (m *VolumeResources) Reset() { *m = VolumeResources{} } func (*VolumeResources) ProtoMessage() {} func (*VolumeResources) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{1} + return fileDescriptor_00212fb1f9d3bf1c, []int{2} } func (m *VolumeResources) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -167,7 +237,7 @@ type EncryptionSpec struct { func (m *EncryptionSpec) Reset() { *m = EncryptionSpec{} } func (*EncryptionSpec) ProtoMessage() {} func (*EncryptionSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{2} + return fileDescriptor_00212fb1f9d3bf1c, []int{3} } func (m *EncryptionSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -215,7 +285,7 @@ type VolumeSpec struct { func (m *VolumeSpec) Reset() { *m = VolumeSpec{} } func (*VolumeSpec) ProtoMessage() {} func (*VolumeSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{3} + return fileDescriptor_00212fb1f9d3bf1c, []int{4} } func (m *VolumeSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -282,7 +352,7 @@ type VolumeStatus struct { func (m *VolumeStatus) Reset() { *m = VolumeStatus{} } func (*VolumeStatus) ProtoMessage() {} func (*VolumeStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{4} + return fileDescriptor_00212fb1f9d3bf1c, []int{5} } func (m *VolumeStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -336,7 +406,7 @@ type Volume struct { func (m *Volume) Reset() { *m = Volume{} } func (*Volume) ProtoMessage() {} func (*Volume) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{5} + return fileDescriptor_00212fb1f9d3bf1c, []int{6} } func (m *Volume) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -396,7 +466,7 @@ type VolumeClassCapabilities struct { func (m *VolumeClassCapabilities) Reset() { *m = VolumeClassCapabilities{} } func (*VolumeClassCapabilities) ProtoMessage() {} func (*VolumeClassCapabilities) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{6} + return fileDescriptor_00212fb1f9d3bf1c, []int{7} } func (m *VolumeClassCapabilities) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -449,7 +519,7 @@ type VolumeClass struct { func (m *VolumeClass) Reset() { *m = VolumeClass{} } func (*VolumeClass) ProtoMessage() {} func (*VolumeClass) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{7} + return fileDescriptor_00212fb1f9d3bf1c, []int{8} } func (m *VolumeClass) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -502,7 +572,7 @@ type VolumeClassStatus struct { func (m *VolumeClassStatus) Reset() { *m = VolumeClassStatus{} } func (*VolumeClassStatus) ProtoMessage() {} func (*VolumeClassStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{8} + return fileDescriptor_00212fb1f9d3bf1c, []int{9} } func (m *VolumeClassStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -557,7 +627,7 @@ type VolumeAccess struct { func (m *VolumeAccess) Reset() { *m = VolumeAccess{} } func (*VolumeAccess) ProtoMessage() {} func (*VolumeAccess) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{9} + return fileDescriptor_00212fb1f9d3bf1c, []int{10} } func (m *VolumeAccess) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -614,6 +684,96 @@ func (m *VolumeAccess) GetSecretData() map[string][]byte { return nil } +type ListEventsRequest struct { + Filter *EventFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListEventsRequest) Reset() { *m = ListEventsRequest{} } +func (*ListEventsRequest) ProtoMessage() {} +func (*ListEventsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{11} +} +func (m *ListEventsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListEventsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListEventsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListEventsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListEventsRequest.Merge(m, src) +} +func (m *ListEventsRequest) XXX_Size() int { + return m.Size() +} +func (m *ListEventsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListEventsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListEventsRequest proto.InternalMessageInfo + +func (m *ListEventsRequest) GetFilter() *EventFilter { + if m != nil { + return m.Filter + } + return nil +} + +type ListEventsResponse struct { + Events []*v1alpha11.Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListEventsResponse) Reset() { *m = ListEventsResponse{} } +func (*ListEventsResponse) ProtoMessage() {} +func (*ListEventsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{12} +} +func (m *ListEventsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListEventsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListEventsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListEventsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListEventsResponse.Merge(m, src) +} +func (m *ListEventsResponse) XXX_Size() int { + return m.Size() +} +func (m *ListEventsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListEventsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListEventsResponse proto.InternalMessageInfo + +func (m *ListEventsResponse) GetEvents() []*v1alpha11.Event { + if m != nil { + return m.Events + } + return nil +} + type ListVolumesRequest struct { Filter *VolumeFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -623,7 +783,7 @@ type ListVolumesRequest struct { func (m *ListVolumesRequest) Reset() { *m = ListVolumesRequest{} } func (*ListVolumesRequest) ProtoMessage() {} func (*ListVolumesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{10} + return fileDescriptor_00212fb1f9d3bf1c, []int{13} } func (m *ListVolumesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -668,7 +828,7 @@ type ListVolumesResponse struct { func (m *ListVolumesResponse) Reset() { *m = ListVolumesResponse{} } func (*ListVolumesResponse) ProtoMessage() {} func (*ListVolumesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{11} + return fileDescriptor_00212fb1f9d3bf1c, []int{14} } func (m *ListVolumesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -713,7 +873,7 @@ type CreateVolumeRequest struct { func (m *CreateVolumeRequest) Reset() { *m = CreateVolumeRequest{} } func (*CreateVolumeRequest) ProtoMessage() {} func (*CreateVolumeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{12} + return fileDescriptor_00212fb1f9d3bf1c, []int{15} } func (m *CreateVolumeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -759,7 +919,7 @@ type ExpandVolumeRequest struct { func (m *ExpandVolumeRequest) Reset() { *m = ExpandVolumeRequest{} } func (*ExpandVolumeRequest) ProtoMessage() {} func (*ExpandVolumeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{13} + return fileDescriptor_00212fb1f9d3bf1c, []int{16} } func (m *ExpandVolumeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -811,7 +971,7 @@ type CreateVolumeResponse struct { func (m *CreateVolumeResponse) Reset() { *m = CreateVolumeResponse{} } func (*CreateVolumeResponse) ProtoMessage() {} func (*CreateVolumeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{14} + return fileDescriptor_00212fb1f9d3bf1c, []int{17} } func (m *CreateVolumeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -855,7 +1015,7 @@ type ExpandVolumeResponse struct { func (m *ExpandVolumeResponse) Reset() { *m = ExpandVolumeResponse{} } func (*ExpandVolumeResponse) ProtoMessage() {} func (*ExpandVolumeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{15} + return fileDescriptor_00212fb1f9d3bf1c, []int{18} } func (m *ExpandVolumeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -893,7 +1053,7 @@ type DeleteVolumeRequest struct { func (m *DeleteVolumeRequest) Reset() { *m = DeleteVolumeRequest{} } func (*DeleteVolumeRequest) ProtoMessage() {} func (*DeleteVolumeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{16} + return fileDescriptor_00212fb1f9d3bf1c, []int{19} } func (m *DeleteVolumeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -937,7 +1097,7 @@ type DeleteVolumeResponse struct { func (m *DeleteVolumeResponse) Reset() { *m = DeleteVolumeResponse{} } func (*DeleteVolumeResponse) ProtoMessage() {} func (*DeleteVolumeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{17} + return fileDescriptor_00212fb1f9d3bf1c, []int{20} } func (m *DeleteVolumeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -974,7 +1134,7 @@ type StatusRequest struct { func (m *StatusRequest) Reset() { *m = StatusRequest{} } func (*StatusRequest) ProtoMessage() {} func (*StatusRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{18} + return fileDescriptor_00212fb1f9d3bf1c, []int{21} } func (m *StatusRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1012,7 +1172,7 @@ type StatusResponse struct { func (m *StatusResponse) Reset() { *m = StatusResponse{} } func (*StatusResponse) ProtoMessage() {} func (*StatusResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_00212fb1f9d3bf1c, []int{19} + return fileDescriptor_00212fb1f9d3bf1c, []int{22} } func (m *StatusResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1052,6 +1212,8 @@ func init() { proto.RegisterEnum("volume.v1alpha1.VolumeState", VolumeState_name, VolumeState_value) proto.RegisterType((*VolumeFilter)(nil), "volume.v1alpha1.VolumeFilter") proto.RegisterMapType((map[string]string)(nil), "volume.v1alpha1.VolumeFilter.LabelSelectorEntry") + proto.RegisterType((*EventFilter)(nil), "volume.v1alpha1.EventFilter") + proto.RegisterMapType((map[string]string)(nil), "volume.v1alpha1.EventFilter.LabelSelectorEntry") proto.RegisterType((*VolumeResources)(nil), "volume.v1alpha1.VolumeResources") proto.RegisterType((*EncryptionSpec)(nil), "volume.v1alpha1.EncryptionSpec") proto.RegisterMapType((map[string][]byte)(nil), "volume.v1alpha1.EncryptionSpec.SecretDataEntry") @@ -1064,6 +1226,8 @@ func init() { proto.RegisterType((*VolumeAccess)(nil), "volume.v1alpha1.VolumeAccess") proto.RegisterMapType((map[string]string)(nil), "volume.v1alpha1.VolumeAccess.AttributesEntry") proto.RegisterMapType((map[string][]byte)(nil), "volume.v1alpha1.VolumeAccess.SecretDataEntry") + proto.RegisterType((*ListEventsRequest)(nil), "volume.v1alpha1.ListEventsRequest") + proto.RegisterType((*ListEventsResponse)(nil), "volume.v1alpha1.ListEventsResponse") proto.RegisterType((*ListVolumesRequest)(nil), "volume.v1alpha1.ListVolumesRequest") proto.RegisterType((*ListVolumesResponse)(nil), "volume.v1alpha1.ListVolumesResponse") proto.RegisterType((*CreateVolumeRequest)(nil), "volume.v1alpha1.CreateVolumeRequest") @@ -1079,73 +1243,81 @@ func init() { func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) } var fileDescriptor_00212fb1f9d3bf1c = []byte{ - // 1045 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x6e, 0xdb, 0x36, - 0x14, 0xb6, 0xec, 0xc4, 0x4b, 0x8e, 0x1d, 0xc7, 0xa5, 0x8d, 0xd6, 0x70, 0x5b, 0x37, 0x50, 0x37, - 0x20, 0x18, 0x10, 0x6b, 0xf1, 0xb0, 0x61, 0x1d, 0xb0, 0x65, 0x4e, 0xe2, 0x76, 0x41, 0x9d, 0xa4, - 0x50, 0xb0, 0x14, 0x28, 0x30, 0x18, 0xb4, 0xcc, 0x3a, 0xdc, 0x64, 0x49, 0x15, 0x29, 0x63, 0xbe, - 0xdb, 0x23, 0xec, 0x15, 0x76, 0xbd, 0x27, 0xe8, 0x1b, 0xf4, 0x72, 0x37, 0x03, 0x06, 0xec, 0x66, - 0xcd, 0x5e, 0x64, 0x10, 0x49, 0x29, 0x92, 0xff, 0x92, 0xa0, 0x77, 0xe4, 0xf1, 0x77, 0x3e, 0x7e, - 0xe7, 0xf0, 0xe3, 0xb1, 0x60, 0x1d, 0x7b, 0xb4, 0xe9, 0xf9, 0x2e, 0x77, 0xd1, 0xe6, 0xd8, 0xb5, - 0x83, 0x11, 0x69, 0x8e, 0x77, 0xb1, 0xed, 0x5d, 0xe0, 0xdd, 0xfa, 0xce, 0x90, 0xf2, 0x8b, 0xa0, - 0xdf, 0xb4, 0xdc, 0x91, 0x31, 0x74, 0x87, 0xae, 0x21, 0x70, 0xfd, 0xe0, 0xb5, 0xd8, 0x89, 0x8d, - 0x58, 0xc9, 0xfc, 0x7a, 0x3b, 0x01, 0xa7, 0xbe, 0xeb, 0x58, 0xae, 0x4f, 0x76, 0x06, 0x64, 0x1c, - 0x6f, 0x0c, 0xea, 0x53, 0x03, 0x7b, 0x94, 0x19, 0x23, 0xc2, 0xb1, 0x11, 0x9d, 0x63, 0xc4, 0x12, - 0xf4, 0xb7, 0x1a, 0x14, 0xcf, 0x85, 0x8a, 0xa7, 0xd4, 0xe6, 0xc4, 0x47, 0x25, 0xc8, 0xd2, 0x41, - 0x4d, 0xdb, 0xd2, 0xb6, 0xd7, 0xcd, 0x2c, 0x1d, 0xa0, 0x97, 0x50, 0xb2, 0x71, 0x9f, 0xd8, 0x3d, - 0x46, 0x6c, 0x62, 0x71, 0xd7, 0xaf, 0x65, 0xb7, 0x72, 0xdb, 0x85, 0xd6, 0x67, 0xcd, 0x29, 0xf1, - 0xcd, 0x24, 0x4d, 0xb3, 0x1b, 0xe6, 0x9c, 0xa9, 0x94, 0x8e, 0xc3, 0xfd, 0x89, 0xb9, 0x61, 0x27, - 0x63, 0xf5, 0xef, 0x00, 0xcd, 0x82, 0x50, 0x19, 0x72, 0x3f, 0x93, 0x89, 0x3a, 0x3f, 0x5c, 0xa2, - 0x2a, 0xac, 0x8e, 0xb1, 0x1d, 0x90, 0x5a, 0x56, 0xc4, 0xe4, 0xe6, 0xeb, 0xec, 0x57, 0x9a, 0xfe, - 0x25, 0x6c, 0xca, 0x33, 0x4d, 0xc2, 0xdc, 0xc0, 0xb7, 0x08, 0x43, 0x8f, 0x61, 0x83, 0x71, 0xd7, - 0xc7, 0x43, 0xd2, 0xeb, 0x4f, 0x38, 0x61, 0x82, 0x28, 0x67, 0x16, 0x55, 0x70, 0x3f, 0x8c, 0xe9, - 0xbf, 0x6b, 0x50, 0xea, 0x38, 0x96, 0x3f, 0xf1, 0x38, 0x75, 0x9d, 0x33, 0x8f, 0x58, 0xe8, 0x05, - 0x14, 0x18, 0xb1, 0x7c, 0xc2, 0x7b, 0x03, 0xcc, 0x71, 0x4d, 0x13, 0x25, 0x1a, 0x33, 0x25, 0xa6, - 0xb3, 0x9a, 0x67, 0x22, 0xe5, 0x10, 0x73, 0x2c, 0x2b, 0x04, 0x16, 0x07, 0xea, 0xdf, 0xc0, 0xe6, - 0xd4, 0xcf, 0xd7, 0xd5, 0x56, 0x4c, 0xd6, 0xf6, 0x56, 0x03, 0x90, 0xc5, 0x09, 0x7d, 0x55, 0x58, - 0xa5, 0x23, 0x3c, 0x24, 0x2a, 0x59, 0x6e, 0xc2, 0xa8, 0x65, 0x63, 0xc6, 0xa2, 0xd6, 0x88, 0x0d, - 0xfa, 0x16, 0xd6, 0xfd, 0xa8, 0x21, 0xb5, 0xdc, 0x96, 0xb6, 0x5d, 0x68, 0x6d, 0x2d, 0xb8, 0xac, - 0xb8, 0x71, 0xe6, 0x55, 0x0a, 0xda, 0x03, 0x20, 0x71, 0x9d, 0xb5, 0x15, 0x41, 0xf0, 0xe8, 0x9a, - 0x56, 0x98, 0x89, 0x14, 0x7d, 0x12, 0x59, 0xea, 0x8c, 0x63, 0x1e, 0x30, 0xd4, 0x82, 0x55, 0xc6, - 0x31, 0x97, 0xe2, 0x4b, 0xad, 0x07, 0x0b, 0xc4, 0x84, 0x68, 0x62, 0x4a, 0x28, 0xfa, 0x02, 0xf2, - 0xd8, 0xb2, 0x88, 0xaa, 0xad, 0xd0, 0x7a, 0xb8, 0x20, 0xa9, 0x2d, 0x40, 0xa6, 0x02, 0xeb, 0x7f, - 0x68, 0x90, 0x97, 0x3f, 0xa0, 0x27, 0xb0, 0x16, 0x9a, 0x5e, 0xdd, 0xa7, 0xe4, 0x08, 0x03, 0x57, - 0x0c, 0xa7, 0xfd, 0x9f, 0x88, 0xc5, 0x8f, 0x15, 0xc8, 0x8c, 0xe1, 0xc8, 0x80, 0x15, 0xe6, 0x11, - 0x4b, 0x1d, 0x7d, 0x7f, 0x91, 0xde, 0xb0, 0x6e, 0x01, 0x0c, 0xd5, 0x32, 0x51, 0xab, 0xea, 0xf7, - 0xc3, 0x25, 0x25, 0x06, 0xcc, 0x54, 0x60, 0x7d, 0x0f, 0xee, 0xc9, 0xf8, 0x41, 0x78, 0x71, 0x07, - 0xd8, 0xc3, 0x7d, 0x6a, 0x53, 0x4e, 0x09, 0x0b, 0xbd, 0xc2, 0xbd, 0xc8, 0xbe, 0xe1, 0x12, 0x21, - 0x58, 0xa1, 0xae, 0x27, 0xfb, 0x91, 0x33, 0xc5, 0x5a, 0x77, 0xa1, 0x90, 0x20, 0x08, 0x21, 0x0e, - 0x1e, 0x45, 0x26, 0x11, 0x6b, 0xd4, 0x85, 0xa2, 0x95, 0x20, 0x56, 0x35, 0x6d, 0x2f, 0x10, 0x38, - 0x23, 0xc4, 0x4c, 0x65, 0xeb, 0x1e, 0xdc, 0x49, 0x00, 0xd5, 0xfd, 0xee, 0x41, 0x51, 0xb2, 0xf5, - 0xa4, 0x1b, 0x65, 0xb7, 0x1f, 0x2c, 0x3b, 0xc2, 0x2c, 0x8c, 0x13, 0xba, 0xeb, 0xb0, 0xf6, 0x26, - 0xc0, 0x0e, 0xa7, 0x7c, 0xa2, 0xca, 0x8b, 0xf7, 0xfa, 0x3f, 0xd9, 0xc8, 0x4d, 0xf2, 0xaa, 0xd1, - 0x5d, 0xc8, 0x0f, 0x7c, 0x3a, 0x26, 0xbe, 0x2a, 0x53, 0xed, 0xc2, 0xf8, 0x05, 0x76, 0x06, 0x76, - 0x34, 0x28, 0xd4, 0x0e, 0x1d, 0x03, 0x60, 0xce, 0x7d, 0xda, 0x0f, 0xb8, 0x78, 0x0f, 0xe1, 0xcb, - 0xde, 0x59, 0xea, 0xa6, 0x66, 0x3b, 0xc6, 0xab, 0x77, 0x7d, 0x45, 0x80, 0x4e, 0xd2, 0x93, 0x62, - 0xe5, 0x26, 0x7c, 0xd7, 0xcc, 0x89, 0xa9, 0xe3, 0x6e, 0x33, 0x03, 0x3f, 0x74, 0xcc, 0x3c, 0x07, - 0xd4, 0xa5, 0x8c, 0x4b, 0xb5, 0xcc, 0x24, 0x6f, 0x02, 0xc2, 0x78, 0x68, 0xe7, 0xd7, 0x62, 0x8c, - 0xc7, 0x0f, 0x67, 0xd9, 0xac, 0x37, 0x15, 0x58, 0xff, 0x1e, 0x2a, 0x29, 0x32, 0xe6, 0xb9, 0x0e, - 0x23, 0x68, 0x17, 0x3e, 0x92, 0xe9, 0x4c, 0xcd, 0xd5, 0x7b, 0x8b, 0xa6, 0x51, 0x84, 0xd3, 0x9f, - 0x42, 0xe5, 0xc0, 0x27, 0x98, 0x93, 0x68, 0x4c, 0x49, 0x5d, 0x06, 0xe4, 0x25, 0x42, 0xe9, 0x5a, - 0x48, 0xa4, 0x60, 0xba, 0x0f, 0x95, 0xce, 0x2f, 0x1e, 0x76, 0x06, 0x69, 0x9e, 0xfb, 0xb0, 0xae, - 0x0c, 0x1b, 0xff, 0xd5, 0xad, 0xc9, 0xc0, 0xd1, 0x20, 0x3d, 0x3e, 0xb3, 0xb7, 0x1e, 0x9f, 0xfa, - 0x33, 0xa8, 0xa6, 0xb5, 0xab, 0x36, 0xdc, 0x5a, 0xfc, 0x5d, 0xa8, 0xa6, 0xc5, 0x4b, 0x22, 0xbd, - 0x05, 0x95, 0x43, 0x62, 0x93, 0xe9, 0xe6, 0x2c, 0x2b, 0x2a, 0xe4, 0x4a, 0xe7, 0x28, 0xae, 0x4d, - 0xd8, 0x50, 0x33, 0x49, 0xb2, 0xe8, 0x03, 0x28, 0x45, 0x01, 0xa5, 0xdb, 0x84, 0x4a, 0xf2, 0x75, - 0xf7, 0xd4, 0xa0, 0x93, 0x57, 0xa9, 0x2f, 0x7b, 0xe4, 0x8a, 0xe8, 0xce, 0x78, 0x3a, 0xf4, 0xe9, - 0x51, 0x34, 0xb7, 0xc4, 0xcc, 0x47, 0x08, 0x4a, 0xe7, 0xa7, 0xdd, 0x1f, 0x8e, 0x3b, 0xbd, 0x17, - 0x9d, 0x93, 0xc3, 0xa3, 0x93, 0x67, 0xe5, 0x0c, 0xaa, 0x42, 0x59, 0xc5, 0xda, 0xe7, 0xed, 0xa3, - 0x6e, 0x7b, 0xbf, 0xdb, 0x29, 0x6b, 0xa8, 0x0c, 0x45, 0x15, 0xed, 0x98, 0xe6, 0xa9, 0x59, 0xce, - 0xb6, 0xfe, 0xca, 0xc1, 0x86, 0x2a, 0x2a, 0x70, 0x38, 0x1d, 0x11, 0xf4, 0x0a, 0x0a, 0x09, 0x1b, - 0xa2, 0xc7, 0x33, 0x12, 0x67, 0x1d, 0x5f, 0xff, 0x78, 0x39, 0x48, 0x75, 0x2b, 0x83, 0x7e, 0x84, - 0x62, 0xf2, 0x72, 0xd1, 0x6c, 0xde, 0x1c, 0xdf, 0xd6, 0x3f, 0xb9, 0x06, 0x95, 0xa4, 0x4f, 0x5e, - 0xf9, 0x1c, 0xfa, 0x39, 0x76, 0x9e, 0x43, 0x3f, 0xd7, 0x37, 0x82, 0x3e, 0xe9, 0x82, 0x39, 0xf4, - 0x73, 0x8c, 0x35, 0x87, 0x7e, 0xae, 0x95, 0x32, 0xe8, 0x39, 0xe4, 0xd5, 0x3f, 0x42, 0x63, 0x26, - 0x25, 0xe5, 0xb2, 0xfa, 0xa3, 0x85, 0xbf, 0x47, 0x64, 0xfb, 0x2f, 0xdf, 0xbd, 0x6f, 0x68, 0x7f, - 0xbf, 0x6f, 0x64, 0x7e, 0xbd, 0x6c, 0x68, 0xef, 0x2e, 0x1b, 0xda, 0x9f, 0x97, 0x0d, 0xed, 0xdf, - 0xcb, 0x86, 0xf6, 0xdb, 0x7f, 0x8d, 0xcc, 0xab, 0x27, 0x37, 0xff, 0xf2, 0x95, 0x27, 0xc5, 0xdf, - 0xbe, 0xfd, 0xbc, 0xf8, 0xf0, 0xfd, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x30, 0x73, 0x90, - 0x3f, 0x88, 0x0b, 0x00, 0x00, + // 1175 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x41, 0x4f, 0x1b, 0xc7, + 0x17, 0x67, 0x6d, 0xe2, 0x7f, 0x78, 0x36, 0xc6, 0x0c, 0xfc, 0x13, 0xe4, 0x04, 0x07, 0x6d, 0x52, + 0x09, 0x55, 0xc2, 0x2e, 0x6e, 0x53, 0x35, 0x95, 0x5a, 0x6a, 0xc0, 0xa4, 0x28, 0x06, 0xa2, 0x25, + 0x05, 0x29, 0x52, 0x65, 0x8d, 0xd7, 0x03, 0x4c, 0xbb, 0xde, 0xdd, 0xec, 0xcc, 0x5a, 0xf5, 0xad, + 0xb7, 0x5e, 0xfb, 0x15, 0x7a, 0xae, 0xd4, 0x7b, 0xbe, 0x41, 0x8e, 0x3d, 0x56, 0xea, 0xa5, 0xa1, + 0x5f, 0xa4, 0xda, 0x99, 0xd9, 0x65, 0xd7, 0xde, 0x35, 0xa0, 0xf6, 0x36, 0xf3, 0xfc, 0x7b, 0xbf, + 0xf9, 0xbd, 0x37, 0xef, 0xbd, 0xf1, 0xc2, 0x1c, 0x76, 0x69, 0xdd, 0xf5, 0x1c, 0xee, 0xa0, 0x85, + 0xa1, 0x63, 0xf9, 0x03, 0x52, 0x1f, 0x6e, 0x62, 0xcb, 0xbd, 0xc0, 0x9b, 0xd5, 0x8d, 0x73, 0xca, + 0x2f, 0xfc, 0x5e, 0xdd, 0x74, 0x06, 0x8d, 0x73, 0xe7, 0xdc, 0x69, 0x08, 0x5c, 0xcf, 0x3f, 0x13, + 0x3b, 0xb1, 0x11, 0x2b, 0xe9, 0x5f, 0x6d, 0xc5, 0xe0, 0xd4, 0x73, 0x6c, 0xd3, 0xf1, 0xc8, 0x46, + 0x9f, 0x0c, 0xa3, 0x4d, 0x83, 0x7a, 0xb4, 0x81, 0x5d, 0xca, 0x1a, 0x03, 0xc2, 0x71, 0x23, 0x3c, + 0xa7, 0x11, 0x49, 0xa8, 0x6e, 0xdf, 0x9c, 0x82, 0x0c, 0x89, 0xcd, 0x53, 0x38, 0xf4, 0xb7, 0x1a, + 0x94, 0x4e, 0x44, 0x24, 0x7b, 0xd4, 0xe2, 0xc4, 0x43, 0x65, 0xc8, 0xd1, 0xfe, 0x8a, 0xb6, 0xa6, + 0xad, 0xcf, 0x19, 0x39, 0xda, 0x47, 0xa7, 0x50, 0xb6, 0x70, 0x8f, 0x58, 0x5d, 0x46, 0x2c, 0x62, + 0x72, 0xc7, 0x5b, 0xc9, 0xad, 0xe5, 0xd7, 0x8b, 0xcd, 0x8f, 0xea, 0x63, 0x09, 0xa8, 0xc7, 0x69, + 0xea, 0x9d, 0xc0, 0xe7, 0x58, 0xb9, 0xb4, 0x6d, 0xee, 0x8d, 0x8c, 0x79, 0x2b, 0x6e, 0xab, 0x7e, + 0x05, 0x68, 0x12, 0x84, 0x2a, 0x90, 0xff, 0x9e, 0x8c, 0xd4, 0xf9, 0xc1, 0x12, 0x2d, 0xc3, 0x9d, + 0x21, 0xb6, 0x7c, 0xb2, 0x92, 0x13, 0x36, 0xb9, 0xf9, 0x3c, 0xf7, 0x99, 0xa6, 0xff, 0x94, 0x83, + 0x62, 0x3b, 0x08, 0x2c, 0x43, 0xfa, 0x49, 0x86, 0xf4, 0xc6, 0x84, 0xf4, 0x18, 0xcb, 0xf5, 0xca, + 0xd1, 0x3a, 0x54, 0x44, 0x3e, 0x59, 0xf7, 0xcc, 0x73, 0x06, 0x5d, 0x4e, 0x07, 0x64, 0x25, 0xbf, + 0xa6, 0xad, 0xe7, 0x8d, 0xb2, 0xb4, 0xef, 0x79, 0xce, 0xe0, 0x15, 0x1d, 0x10, 0xf4, 0x04, 0x94, + 0xa5, 0xcb, 0x1d, 0x89, 0x9b, 0x15, 0xb8, 0x92, 0xb4, 0xbe, 0x72, 0x02, 0xd4, 0x7f, 0x90, 0x89, + 0x4f, 0x61, 0x41, 0x66, 0xdf, 0x20, 0xcc, 0xf1, 0x3d, 0x93, 0x30, 0xf4, 0x18, 0xe6, 0x19, 0x77, + 0x3c, 0x7c, 0x4e, 0xba, 0xbd, 0x11, 0x27, 0x4c, 0x10, 0xe5, 0x8d, 0x92, 0x32, 0x6e, 0x07, 0x36, + 0xfd, 0x17, 0x0d, 0xca, 0x6d, 0xdb, 0xf4, 0x46, 0x2e, 0xa7, 0x8e, 0x7d, 0xec, 0x12, 0x13, 0xbd, + 0x84, 0x22, 0x23, 0xa6, 0x47, 0x78, 0xb7, 0x8f, 0x39, 0x5e, 0xd1, 0xb2, 0x32, 0x96, 0xf0, 0xaa, + 0x1f, 0x0b, 0x97, 0x5d, 0xcc, 0xb1, 0xcc, 0x18, 0xb0, 0xc8, 0x50, 0xfd, 0x02, 0x16, 0xc6, 0x7e, + 0xbe, 0x2e, 0xb6, 0x52, 0x3c, 0xb6, 0xb7, 0x1a, 0x80, 0x0c, 0x4e, 0xe8, 0x5b, 0x86, 0x3b, 0x74, + 0x80, 0xcf, 0x89, 0x72, 0x96, 0x9b, 0xc0, 0x6a, 0x5a, 0x98, 0xb1, 0x30, 0x35, 0x62, 0x83, 0xbe, + 0x84, 0x39, 0x2f, 0x4c, 0x88, 0xb8, 0xa1, 0x62, 0x73, 0x2d, 0xa3, 0x6c, 0xa3, 0xc4, 0x19, 0x57, + 0x2e, 0x68, 0x0b, 0x80, 0x44, 0x71, 0x8a, 0xab, 0x2b, 0x36, 0x1f, 0x5d, 0x93, 0x0a, 0x23, 0xe6, + 0xa2, 0x8f, 0xc2, 0xe6, 0x3a, 0xe6, 0x98, 0xfb, 0x0c, 0x35, 0xe1, 0x0e, 0xe3, 0x98, 0x4b, 0xf1, + 0xe5, 0xe6, 0xc3, 0x0c, 0x31, 0x01, 0x9a, 0x18, 0x12, 0x8a, 0x9e, 0x42, 0x01, 0x9b, 0x26, 0x51, + 0xb1, 0x15, 0x9b, 0xab, 0x19, 0x4e, 0x2d, 0x01, 0x32, 0x14, 0x58, 0xff, 0x55, 0x83, 0x82, 0xfc, + 0x01, 0x3d, 0x83, 0xbb, 0xc1, 0x08, 0x51, 0xf7, 0x29, 0x39, 0x02, 0xc3, 0x15, 0xc3, 0x51, 0xef, + 0x3b, 0x62, 0xf2, 0x03, 0x05, 0x32, 0x22, 0x38, 0x6a, 0xc0, 0x2c, 0x73, 0x89, 0xa9, 0x8e, 0x7e, + 0x90, 0xa5, 0x37, 0x88, 0x5b, 0x00, 0x03, 0xb5, 0x4c, 0xc4, 0xaa, 0xf2, 0xbd, 0x3a, 0x25, 0x44, + 0x9f, 0x19, 0x0a, 0xac, 0x6f, 0xc1, 0x7d, 0x69, 0xdf, 0x09, 0x2e, 0x6e, 0x07, 0xbb, 0xb8, 0x47, + 0x2d, 0xca, 0x29, 0x61, 0x41, 0xad, 0x70, 0x37, 0x2c, 0xdf, 0x60, 0x89, 0x10, 0xcc, 0x52, 0xc7, + 0x95, 0xf9, 0xc8, 0x1b, 0x62, 0xad, 0x3b, 0x50, 0x8c, 0x11, 0x04, 0x10, 0x1b, 0x0f, 0xc2, 0x22, + 0x11, 0x6b, 0xd4, 0x81, 0x92, 0x19, 0x23, 0x56, 0x31, 0xad, 0x67, 0x08, 0x9c, 0x10, 0x62, 0x24, + 0xbc, 0x75, 0x17, 0x16, 0x63, 0x40, 0x75, 0xbf, 0x5b, 0x50, 0x92, 0x6c, 0x5d, 0x59, 0x8d, 0x32, + 0xdb, 0x0f, 0xa7, 0x1d, 0x61, 0x14, 0x87, 0x31, 0xdd, 0x55, 0xb8, 0xfb, 0xc6, 0xc7, 0x36, 0xa7, + 0x7c, 0xa4, 0xc2, 0x8b, 0xf6, 0xfa, 0x9f, 0xb9, 0xb0, 0x9a, 0xe4, 0x55, 0xa3, 0x7b, 0x50, 0xe8, + 0x7b, 0x74, 0x48, 0x3c, 0x15, 0xa6, 0xda, 0x05, 0xf6, 0x0b, 0x6c, 0xf7, 0xad, 0x70, 0x50, 0xa8, + 0x1d, 0x3a, 0x00, 0xc0, 0x9c, 0x7b, 0xb4, 0xe7, 0x73, 0xd1, 0x0f, 0x41, 0x67, 0x6f, 0x4c, 0xad, + 0xa6, 0x7a, 0x2b, 0xc2, 0xab, 0xbe, 0xbe, 0x22, 0x40, 0x87, 0xc9, 0x49, 0x31, 0x7b, 0x13, 0xbe, + 0x6b, 0xe6, 0xc4, 0xd8, 0x71, 0xb7, 0x99, 0x81, 0xff, 0x76, 0xcc, 0xec, 0xc3, 0x62, 0x87, 0x32, + 0x2e, 0x5e, 0x02, 0x66, 0x90, 0x37, 0x3e, 0x61, 0x1c, 0x7d, 0x02, 0x85, 0x33, 0xf1, 0x2a, 0x64, + 0xde, 0x64, 0xec, 0xe5, 0x30, 0x14, 0x56, 0xdf, 0x01, 0x14, 0xa7, 0x62, 0xae, 0x63, 0x33, 0x82, + 0x36, 0xa0, 0x20, 0xa7, 0xbe, 0x9a, 0xa9, 0xff, 0xaf, 0x8b, 0xed, 0x18, 0x95, 0xa1, 0x40, 0xfa, + 0x0b, 0x49, 0x22, 0xb3, 0x17, 0x09, 0x7a, 0x3a, 0x26, 0x68, 0x75, 0xea, 0x2b, 0x1c, 0x29, 0xfa, + 0x1a, 0x96, 0x12, 0x64, 0x4a, 0xd2, 0x26, 0xfc, 0x4f, 0xba, 0x87, 0x9a, 0xee, 0x67, 0x4d, 0xc7, + 0x10, 0xa7, 0xef, 0xc1, 0xd2, 0x8e, 0x47, 0x30, 0x27, 0xe1, 0xd8, 0x94, 0xba, 0x1a, 0x50, 0x90, + 0x08, 0xa5, 0x2b, 0x93, 0x48, 0xc1, 0x74, 0x0f, 0x96, 0xda, 0x3f, 0xb8, 0xd8, 0xee, 0x27, 0x79, + 0x1e, 0xc0, 0x9c, 0x6a, 0xa0, 0xe8, 0x25, 0xbf, 0x2b, 0x0d, 0xfb, 0xfd, 0xe4, 0x38, 0xcf, 0xdd, + 0x7a, 0x9c, 0xeb, 0xcf, 0x61, 0x39, 0xa9, 0x5d, 0xa5, 0xe1, 0xd6, 0xe2, 0xef, 0xc1, 0x72, 0x52, + 0xbc, 0x24, 0xd2, 0x9b, 0xb0, 0xb4, 0x4b, 0x2c, 0x32, 0x9e, 0x9c, 0x69, 0x41, 0x05, 0x5c, 0x49, + 0x1f, 0xc5, 0xb5, 0x00, 0xf3, 0x6a, 0x46, 0x4a, 0x16, 0xbd, 0x0f, 0xe5, 0xd0, 0xa0, 0x74, 0x1b, + 0xb0, 0x14, 0x9f, 0x36, 0x5d, 0x35, 0x78, 0xe5, 0x55, 0xea, 0xd3, 0x86, 0x8e, 0x22, 0x5a, 0x1c, + 0x8e, 0x9b, 0x3e, 0xdc, 0x0f, 0xe7, 0xa8, 0x78, 0x83, 0x10, 0x82, 0xf2, 0xc9, 0x51, 0xe7, 0x9b, + 0x83, 0x76, 0xf7, 0x65, 0xfb, 0x70, 0x77, 0xff, 0xf0, 0x79, 0x65, 0x06, 0x2d, 0x43, 0x45, 0xd9, + 0x5a, 0x27, 0xad, 0xfd, 0x4e, 0x6b, 0xbb, 0xd3, 0xae, 0x68, 0xa8, 0x02, 0x25, 0x65, 0x6d, 0x1b, + 0xc6, 0x91, 0x51, 0xc9, 0x35, 0x7f, 0x9b, 0x85, 0x79, 0x15, 0x94, 0x6f, 0x07, 0x7f, 0x7e, 0xd0, + 0x29, 0xc0, 0x55, 0x63, 0xa0, 0x49, 0x85, 0x13, 0x0d, 0x58, 0x7d, 0x3c, 0x15, 0xa3, 0x52, 0x35, + 0x83, 0x5e, 0x43, 0x31, 0x56, 0xdf, 0x28, 0xdd, 0x2b, 0xd9, 0x4a, 0xd5, 0x27, 0xd3, 0x41, 0x11, + 0xf7, 0xb7, 0x50, 0x8a, 0x57, 0x0d, 0x9a, 0xf4, 0x4b, 0x69, 0x88, 0xea, 0x07, 0xd7, 0xa0, 0xe2, + 0xf4, 0xf1, 0x5a, 0x4a, 0xa1, 0x4f, 0xe9, 0x93, 0x14, 0xfa, 0xd4, 0x82, 0x14, 0xf4, 0xf1, 0xf2, + 0x4a, 0xa1, 0x4f, 0xa9, 0xd8, 0x14, 0xfa, 0xd4, 0x1a, 0x9d, 0x41, 0x2f, 0xa0, 0xa0, 0x9e, 0xbe, + 0xda, 0x84, 0x4b, 0xa2, 0x7c, 0xab, 0x8f, 0x32, 0x7f, 0x0f, 0xc9, 0xb6, 0x4f, 0xdf, 0xbd, 0xaf, + 0x69, 0x7f, 0xbc, 0xaf, 0xcd, 0xfc, 0x78, 0x59, 0xd3, 0xde, 0x5d, 0xd6, 0xb4, 0xdf, 0x2f, 0x6b, + 0xda, 0x5f, 0x97, 0x35, 0xed, 0xe7, 0xbf, 0x6b, 0x33, 0xaf, 0x9f, 0xdd, 0xfc, 0x6b, 0x47, 0x9e, + 0x14, 0x7d, 0xee, 0xf4, 0x0a, 0xe2, 0x5b, 0xe7, 0xe3, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x94, + 0x3f, 0x92, 0x6e, 0xbf, 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1160,6 +1332,7 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type VolumeRuntimeClient interface { + ListEvents(ctx context.Context, in *ListEventsRequest, opts ...grpc.CallOption) (*ListEventsResponse, error) ListVolumes(ctx context.Context, in *ListVolumesRequest, opts ...grpc.CallOption) (*ListVolumesResponse, error) CreateVolume(ctx context.Context, in *CreateVolumeRequest, opts ...grpc.CallOption) (*CreateVolumeResponse, error) ExpandVolume(ctx context.Context, in *ExpandVolumeRequest, opts ...grpc.CallOption) (*ExpandVolumeResponse, error) @@ -1175,6 +1348,15 @@ func NewVolumeRuntimeClient(cc *grpc.ClientConn) VolumeRuntimeClient { return &volumeRuntimeClient{cc} } +func (c *volumeRuntimeClient) ListEvents(ctx context.Context, in *ListEventsRequest, opts ...grpc.CallOption) (*ListEventsResponse, error) { + out := new(ListEventsResponse) + err := c.cc.Invoke(ctx, "/volume.v1alpha1.VolumeRuntime/ListEvents", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *volumeRuntimeClient) ListVolumes(ctx context.Context, in *ListVolumesRequest, opts ...grpc.CallOption) (*ListVolumesResponse, error) { out := new(ListVolumesResponse) err := c.cc.Invoke(ctx, "/volume.v1alpha1.VolumeRuntime/ListVolumes", in, out, opts...) @@ -1222,6 +1404,7 @@ func (c *volumeRuntimeClient) Status(ctx context.Context, in *StatusRequest, opt // VolumeRuntimeServer is the server API for VolumeRuntime service. type VolumeRuntimeServer interface { + ListEvents(context.Context, *ListEventsRequest) (*ListEventsResponse, error) ListVolumes(context.Context, *ListVolumesRequest) (*ListVolumesResponse, error) CreateVolume(context.Context, *CreateVolumeRequest) (*CreateVolumeResponse, error) ExpandVolume(context.Context, *ExpandVolumeRequest) (*ExpandVolumeResponse, error) @@ -1233,6 +1416,9 @@ type VolumeRuntimeServer interface { type UnimplementedVolumeRuntimeServer struct { } +func (*UnimplementedVolumeRuntimeServer) ListEvents(ctx context.Context, req *ListEventsRequest) (*ListEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListEvents not implemented") +} func (*UnimplementedVolumeRuntimeServer) ListVolumes(ctx context.Context, req *ListVolumesRequest) (*ListVolumesResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListVolumes not implemented") } @@ -1253,6 +1439,24 @@ func RegisterVolumeRuntimeServer(s *grpc.Server, srv VolumeRuntimeServer) { s.RegisterService(&_VolumeRuntime_serviceDesc, srv) } +func _VolumeRuntime_ListEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VolumeRuntimeServer).ListEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/volume.v1alpha1.VolumeRuntime/ListEvents", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VolumeRuntimeServer).ListEvents(ctx, req.(*ListEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VolumeRuntime_ListVolumes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListVolumesRequest) if err := dec(in); err != nil { @@ -1347,6 +1551,10 @@ var _VolumeRuntime_serviceDesc = grpc.ServiceDesc{ ServiceName: "volume.v1alpha1.VolumeRuntime", HandlerType: (*VolumeRuntimeServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "ListEvents", + Handler: _VolumeRuntime_ListEvents_Handler, + }, { MethodName: "ListVolumes", Handler: _VolumeRuntime_ListVolumes_Handler, @@ -1421,6 +1629,65 @@ func (m *VolumeFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EventFilter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventFilter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventsToTime != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.EventsToTime)) + i-- + dAtA[i] = 0x20 + } + if m.EventsFromTime != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.EventsFromTime)) + i-- + dAtA[i] = 0x18 + } + if len(m.LabelSelector) > 0 { + for k := range m.LabelSelector { + v := m.LabelSelector[k] + baseI := i + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintApi(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintApi(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintApi(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintApi(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *VolumeResources) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1845,7 +2112,7 @@ func (m *VolumeAccess) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ListVolumesRequest) Marshal() (dAtA []byte, err error) { +func (m *ListEventsRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1855,12 +2122,12 @@ func (m *ListVolumesRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ListVolumesRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *ListEventsRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ListVolumesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ListEventsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -1880,7 +2147,7 @@ func (m *ListVolumesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ListVolumesResponse) Marshal() (dAtA []byte, err error) { +func (m *ListEventsResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1890,20 +2157,20 @@ func (m *ListVolumesResponse) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ListVolumesResponse) MarshalTo(dAtA []byte) (int, error) { +func (m *ListEventsResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ListVolumesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ListEventsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if len(m.Volumes) > 0 { - for iNdEx := len(m.Volumes) - 1; iNdEx >= 0; iNdEx-- { + if len(m.Events) > 0 { + for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { { - size, err := m.Volumes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Events[iNdEx].MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1917,7 +2184,7 @@ func (m *ListVolumesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *CreateVolumeRequest) Marshal() (dAtA []byte, err error) { +func (m *ListVolumesRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1927,19 +2194,19 @@ func (m *CreateVolumeRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *CreateVolumeRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *ListVolumesRequest) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *CreateVolumeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ListVolumesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.Volume != nil { + if m.Filter != nil { { - size, err := m.Volume.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.Filter.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -1952,7 +2219,7 @@ func (m *CreateVolumeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *ExpandVolumeRequest) Marshal() (dAtA []byte, err error) { +func (m *ListVolumesResponse) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1962,26 +2229,98 @@ func (m *ExpandVolumeRequest) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *ExpandVolumeRequest) MarshalTo(dAtA []byte) (int, error) { +func (m *ListVolumesResponse) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *ExpandVolumeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *ListVolumesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l - if m.Resources != nil { - { - size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if len(m.Volumes) > 0 { + for iNdEx := len(m.Volumes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Volumes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintApi(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa } - i-- + } + return len(dAtA) - i, nil +} + +func (m *CreateVolumeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CreateVolumeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CreateVolumeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Volume != nil { + { + size, err := m.Volume.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ExpandVolumeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ExpandVolumeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ExpandVolumeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Resources != nil { + { + size, err := m.Resources.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x12 } if len(m.VolumeId) > 0 { @@ -2197,6 +2536,33 @@ func (m *VolumeFilter) Size() (n int) { return n } +func (m *EventFilter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.LabelSelector) > 0 { + for k, v := range m.LabelSelector { + _ = k + _ = v + mapEntrySize := 1 + len(k) + sovApi(uint64(len(k))) + 1 + len(v) + sovApi(uint64(len(v))) + n += mapEntrySize + 1 + sovApi(uint64(mapEntrySize)) + } + } + if m.EventsFromTime != 0 { + n += 1 + sovApi(uint64(m.EventsFromTime)) + } + if m.EventsToTime != 0 { + n += 1 + sovApi(uint64(m.EventsToTime)) + } + return n +} + func (m *VolumeResources) Size() (n int) { if m == nil { return 0 @@ -2377,6 +2743,34 @@ func (m *VolumeAccess) Size() (n int) { return n } +func (m *ListEventsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Filter != nil { + l = m.Filter.Size() + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *ListEventsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Events) > 0 { + for _, e := range m.Events { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + func (m *ListVolumesRequest) Size() (n int) { if m == nil { return 0 @@ -2530,6 +2924,29 @@ func (this *VolumeFilter) String() string { }, "") return s } +func (this *EventFilter) String() string { + if this == nil { + return "nil" + } + keysForLabelSelector := make([]string, 0, len(this.LabelSelector)) + for k, _ := range this.LabelSelector { + keysForLabelSelector = append(keysForLabelSelector, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForLabelSelector) + mapStringForLabelSelector := "map[string]string{" + for _, k := range keysForLabelSelector { + mapStringForLabelSelector += fmt.Sprintf("%v: %v,", k, this.LabelSelector[k]) + } + mapStringForLabelSelector += "}" + s := strings.Join([]string{`&EventFilter{`, + `Id:` + fmt.Sprintf("%v", this.Id) + `,`, + `LabelSelector:` + mapStringForLabelSelector + `,`, + `EventsFromTime:` + fmt.Sprintf("%v", this.EventsFromTime) + `,`, + `EventsToTime:` + fmt.Sprintf("%v", this.EventsToTime) + `,`, + `}`, + }, "") + return s +} func (this *VolumeResources) String() string { if this == nil { return "nil" @@ -2662,6 +3079,31 @@ func (this *VolumeAccess) String() string { }, "") return s } +func (this *ListEventsRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListEventsRequest{`, + `Filter:` + strings.Replace(this.Filter.String(), "EventFilter", "EventFilter", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ListEventsResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForEvents := "[]*Event{" + for _, f := range this.Events { + repeatedStringForEvents += strings.Replace(fmt.Sprintf("%v", f), "Event", "v1alpha11.Event", 1) + "," + } + repeatedStringForEvents += "}" + s := strings.Join([]string{`&ListEventsResponse{`, + `Events:` + repeatedStringForEvents + `,`, + `}`, + }, "") + return s +} func (this *ListVolumesRequest) String() string { if this == nil { return "nil" @@ -2763,22 +3205,231 @@ func (this *StatusResponse) String() string { for _, f := range this.VolumeClassStatus { repeatedStringForVolumeClassStatus += strings.Replace(f.String(), "VolumeClassStatus", "VolumeClassStatus", 1) + "," } - repeatedStringForVolumeClassStatus += "}" - s := strings.Join([]string{`&StatusResponse{`, - `VolumeClassStatus:` + repeatedStringForVolumeClassStatus + `,`, - `}`, - }, "") - return s -} -func valueToStringApi(v interface{}) string { - rv := reflect.ValueOf(v) - if rv.IsNil() { - return "nil" + repeatedStringForVolumeClassStatus += "}" + s := strings.Join([]string{`&StatusResponse{`, + `VolumeClassStatus:` + repeatedStringForVolumeClassStatus + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *VolumeFilter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: VolumeFilter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VolumeFilter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LabelSelector", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LabelSelector == nil { + m.LabelSelector = make(map[string]string) + } + var mapkey string + var mapvalue string + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var stringLenmapvalue uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapvalue |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapvalue := int(stringLenmapvalue) + if intStringLenmapvalue < 0 { + return ErrInvalidLengthApi + } + postStringIndexmapvalue := iNdEx + intStringLenmapvalue + if postStringIndexmapvalue < 0 { + return ErrInvalidLengthApi + } + if postStringIndexmapvalue > l { + return io.ErrUnexpectedEOF + } + mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) + iNdEx = postStringIndexmapvalue + } else { + iNdEx = entryPreIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.LabelSelector[mapkey] = mapvalue + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF } - pv := reflect.Indirect(rv).Interface() - return fmt.Sprintf("*%v", pv) + return nil } -func (m *VolumeFilter) Unmarshal(dAtA []byte) error { +func (m *EventFilter) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -2801,10 +3452,10 @@ func (m *VolumeFilter) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: VolumeFilter: wiretype end group for non-group") + return fmt.Errorf("proto: EventFilter: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: VolumeFilter: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EventFilter: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -2966,6 +3617,44 @@ func (m *VolumeFilter) Unmarshal(dAtA []byte) error { } m.LabelSelector[mapkey] = mapvalue iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EventsFromTime", wireType) + } + m.EventsFromTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EventsFromTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EventsToTime", wireType) + } + m.EventsToTime = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EventsToTime |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipApi(dAtA[iNdEx:]) @@ -4363,6 +5052,176 @@ func (m *VolumeAccess) Unmarshal(dAtA []byte) error { } return nil } +func (m *ListEventsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListEventsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListEventsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Filter == nil { + m.Filter = &EventFilter{} + } + if err := m.Filter.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListEventsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListEventsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListEventsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Events = append(m.Events, &v1alpha11.Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ListVolumesRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/iri/apis/volume/v1alpha1/api.proto b/iri/apis/volume/v1alpha1/api.proto index b03e17b67..285dc4e1a 100644 --- a/iri/apis/volume/v1alpha1/api.proto +++ b/iri/apis/volume/v1alpha1/api.proto @@ -5,6 +5,7 @@ option go_package = "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1/api.proto"; +import "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1/api.proto"; option (gogoproto.goproto_stringer_all) = false; option (gogoproto.stringer_all) = true; @@ -15,6 +16,7 @@ option (gogoproto.unmarshaler_all) = true; option (gogoproto.goproto_unrecognized_all) = false; service VolumeRuntime { + rpc ListEvents(ListEventsRequest) returns (ListEventsResponse) {}; rpc ListVolumes(ListVolumesRequest) returns (ListVolumesResponse) {}; rpc CreateVolume(CreateVolumeRequest) returns (CreateVolumeResponse) {}; rpc ExpandVolume(ExpandVolumeRequest) returns (ExpandVolumeResponse) {}; @@ -28,6 +30,13 @@ message VolumeFilter { map label_selector = 2; } +message EventFilter { + string id = 1; + map label_selector = 2; + int64 events_from_time = 3; + int64 events_to_time = 4; +} + message VolumeResources { int64 storage_bytes = 1; } @@ -82,6 +91,15 @@ enum VolumeState { VOLUME_ERROR = 2; } +message ListEventsRequest { + EventFilter filter = 1; +} + +message ListEventsResponse { + repeated event.v1alpha1.Event events = 1; +} + + message ListVolumesRequest { VolumeFilter filter = 1; } diff --git a/iri/apis/volume/volume.go b/iri/apis/volume/volume.go index ed596bbba..f72dffb77 100644 --- a/iri/apis/volume/volume.go +++ b/iri/apis/volume/volume.go @@ -10,6 +10,7 @@ import ( ) type RuntimeService interface { + ListEvents(context.Context, *api.ListEventsRequest) (*api.ListEventsResponse, error) ListVolumes(context.Context, *api.ListVolumesRequest) (*api.ListVolumesResponse, error) CreateVolume(context.Context, *api.CreateVolumeRequest) (*api.CreateVolumeResponse, error) ExpandVolume(ctx context.Context, request *api.ExpandVolumeRequest) (*api.ExpandVolumeResponse, error) diff --git a/iri/remote/volume/runtime.go b/iri/remote/volume/runtime.go index f605fa4c2..d62c17ea3 100644 --- a/iri/remote/volume/runtime.go +++ b/iri/remote/volume/runtime.go @@ -30,6 +30,9 @@ func NewRemoteRuntime(endpoint string) (volume.RuntimeService, error) { }, nil } +func (r *remoteRuntime) ListEvents(ctx context.Context, req *iri.ListEventsRequest) (*iri.ListEventsResponse, error) { + return r.client.ListEvents(ctx, req) +} func (r *remoteRuntime) ListVolumes(ctx context.Context, request *iri.ListVolumesRequest) (*iri.ListVolumesResponse, error) { return r.client.ListVolumes(ctx, request) } diff --git a/iri/testing/volume/fake.go b/iri/testing/volume/fake.go index 552898fba..724df0a51 100644 --- a/iri/testing/volume/fake.go +++ b/iri/testing/volume/fake.go @@ -9,6 +9,7 @@ import ( "time" "github.com/ironcore-dev/ironcore/broker/common/idgen" + irievent "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -26,6 +27,9 @@ type FakeVolume struct { type FakeVolumeClassStatus struct { iri.VolumeClassStatus } +type FakeEvent struct { + irievent.Event +} type FakeRuntimeService struct { sync.Mutex @@ -34,6 +38,7 @@ type FakeRuntimeService struct { Volumes map[string]*FakeVolume VolumeClassesStatus map[string]*FakeVolumeClassStatus + Events []*FakeEvent } func NewFakeRuntimeService() *FakeRuntimeService { @@ -42,6 +47,7 @@ func NewFakeRuntimeService() *FakeRuntimeService { Volumes: make(map[string]*FakeVolume), VolumeClassesStatus: make(map[string]*FakeVolumeClassStatus), + Events: []*FakeEvent{}, } } @@ -65,6 +71,27 @@ func (r *FakeRuntimeService) SetVolumeClasses(volumeClassStatus []*FakeVolumeCla } } +func (r *FakeRuntimeService) SetEvents(events []*FakeEvent) { + r.Lock() + defer r.Unlock() + + r.Events = events +} + +// ListEvents implements volume.RuntimeService. +func (r *FakeRuntimeService) ListEvents(ctx context.Context, req *iri.ListEventsRequest) (*iri.ListEventsResponse, error) { + r.Lock() + defer r.Unlock() + + var res []*irievent.Event + for _, e := range r.Events { + event := e.Event + res = append(res, &event) + } + + return &iri.ListEventsResponse{Events: res}, nil +} + func (r *FakeRuntimeService) ListVolumes(ctx context.Context, req *iri.ListVolumesRequest) (*iri.ListVolumesResponse, error) { r.Lock() defer r.Unlock() diff --git a/irictl-volume/cmd/irictl-volume/irictlvolume/get/event/event.go b/irictl-volume/cmd/irictl-volume/irictlvolume/get/event/event.go new file mode 100644 index 000000000..812307c5c --- /dev/null +++ b/irictl-volume/cmd/irictl-volume/irictlvolume/get/event/event.go @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package event + +import ( + "context" + "fmt" + "time" + + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + "github.com/ironcore-dev/ironcore/irictl-volume/cmd/irictl-volume/irictlvolume/common" + clicommon "github.com/ironcore-dev/ironcore/irictl/cmd" + "github.com/ironcore-dev/ironcore/irictl/renderer" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + ctrl "sigs.k8s.io/controller-runtime" +) + +type Options struct { + Labels map[string]string + Duration time.Duration +} + +func (o *Options) AddFlags(fs *pflag.FlagSet) { + fs.StringToStringVarP(&o.Labels, "labels", "l", o.Labels, "Labels to filter the events by.") + fs.DurationVarP(&o.Duration, "duration", "d", 60*time.Minute, "Duration to filter the events by.") +} + +func Command(streams clicommon.Streams, clientFactory common.ClientFactory) *cobra.Command { + var ( + opts Options + outputOpts = common.NewOutputOptions() + ) + + cmd := &cobra.Command{ + Use: "events", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + log := ctrl.LoggerFrom(ctx) + + client, cleanup, err := clientFactory.New() + if err != nil { + return err + } + defer func() { + if err := cleanup(); err != nil { + log.Error(err, "Error cleaning up") + } + }() + + render, err := outputOpts.Renderer("table") + if err != nil { + return err + } + + return Run(cmd.Context(), streams, client, render, opts) + }, + } + + outputOpts.AddFlags(cmd.Flags()) + opts.AddFlags(cmd.Flags()) + + return cmd +} + +func Run( + ctx context.Context, + streams clicommon.Streams, + client iri.VolumeRuntimeClient, + render renderer.Renderer, + opts Options, +) error { + var filter *iri.EventFilter = &iri.EventFilter{ + LabelSelector: opts.Labels, + EventsFromTime: time.Now().Add(-1 * opts.Duration).Unix(), + EventsToTime: time.Now().Unix(), + } + + res, err := client.ListEvents(ctx, &iri.ListEventsRequest{Filter: filter}) + if err != nil { + return fmt.Errorf("error listing events: %w", err) + } + + return render.Render(res.Events, streams.Out) +} diff --git a/irictl-volume/cmd/irictl-volume/irictlvolume/get/get.go b/irictl-volume/cmd/irictl-volume/irictlvolume/get/get.go index 4edca2bdb..ca8b01938 100644 --- a/irictl-volume/cmd/irictl-volume/irictlvolume/get/get.go +++ b/irictl-volume/cmd/irictl-volume/irictlvolume/get/get.go @@ -5,6 +5,7 @@ package get import ( "github.com/ironcore-dev/ironcore/irictl-volume/cmd/irictl-volume/irictlvolume/common" + "github.com/ironcore-dev/ironcore/irictl-volume/cmd/irictl-volume/irictlvolume/get/event" "github.com/ironcore-dev/ironcore/irictl-volume/cmd/irictl-volume/irictlvolume/get/status" "github.com/ironcore-dev/ironcore/irictl-volume/cmd/irictl-volume/irictlvolume/get/volume" clicommon "github.com/ironcore-dev/ironcore/irictl/cmd" @@ -19,6 +20,7 @@ func Command(streams clicommon.Streams, clientFactory common.ClientFactory) *cob cmd.AddCommand( volume.Command(streams, clientFactory), status.Command(streams, clientFactory), + event.Command(streams, clientFactory), ) return cmd diff --git a/irictl-volume/tableconverters/event.go b/irictl-volume/tableconverters/event.go new file mode 100644 index 000000000..a4ee9039f --- /dev/null +++ b/irictl-volume/tableconverters/event.go @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package tableconverters + +import ( + iri "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" + "github.com/ironcore-dev/ironcore/irictl/api" + "github.com/ironcore-dev/ironcore/irictl/tableconverter" + volumepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" +) + +const ( + RootVolumeName = "downward-api.volumepoollet.ironcore.dev/root-volume-name" + RootVolumeNamespace = "downward-api.volumepoollet.ironcore.dev/root-volume-namespace" +) + +var ( + eventHeaders = []api.Header{ + {Name: "InvolvedVolumeName"}, + {Name: "Type"}, + {Name: "Reason"}, + {Name: "Message"}, + {Name: "RootVolumeName"}, + {Name: "RootVolumeNamespace"}, + } + + Events = tableconverter.Funcs[*iri.Event]{ + Headers: tableconverter.Headers(eventHeaders), + Rows: tableconverter.SingleRowFrom(func(event *iri.Event) (api.Row, error) { + return api.Row{ + event.Spec.GetInvolvedObjectMeta().Id, + event.Spec.Type, + event.Spec.Reason, + event.Spec.Message, + getRootVolumeName(event.Spec.GetInvolvedObjectMeta().Labels), + getRootVolumeNamespace(event.Spec.GetInvolvedObjectMeta().Labels), + }, nil + }), + } + + EventsSlice = tableconverter.SliceFuncs[*iri.Event](Events) +) + +func getRootVolumeName(labels map[string]string) string { + var rootVolumeName string + rootVolumeName, ok := labels[RootVolumeName] + if !ok { + return labels[volumepoolletv1alpha1.VolumeNameLabel] + } + return rootVolumeName +} +func getRootVolumeNamespace(labels map[string]string) string { + var rootVolumeNamespace string + rootVolumeNamespace, ok := labels[RootVolumeNamespace] + if !ok { + return labels[volumepoolletv1alpha1.VolumeNamespaceLabel] + } + return rootVolumeNamespace +} + +func init() { + RegistryBuilder.Register( + tableconverter.ToTagAndTypedAny[*iri.Event](Events), + tableconverter.ToTagAndTypedAny[[]*iri.Event](EventsSlice), + ) +} diff --git a/poollet/volumepoollet/cmd/volumepoollet/app/app.go b/poollet/volumepoollet/cmd/volumepoollet/app/app.go index 3bf6d604d..fe3bf514b 100644 --- a/poollet/volumepoollet/cmd/volumepoollet/app/app.go +++ b/poollet/volumepoollet/cmd/volumepoollet/app/app.go @@ -21,6 +21,8 @@ import ( volumepoolletconfig "github.com/ironcore-dev/ironcore/poollet/volumepoollet/client/config" "github.com/ironcore-dev/ironcore/poollet/volumepoollet/controllers" "github.com/ironcore-dev/ironcore/poollet/volumepoollet/vcm" + "github.com/ironcore-dev/ironcore/poollet/volumepoollet/vem" + "github.com/ironcore-dev/ironcore/utils/client/config" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -173,6 +175,10 @@ func Run(ctx context.Context, opts Options) error { if err := mgr.Add(volumeClassMapper); err != nil { return fmt.Errorf("error adding volume class mapper: %w", err) } + volumeEventMapper := vem.NewVolumeEventMapper(mgr.GetClient(), volumeRuntime, mgr.GetEventRecorderFor("volume-cluster-events"), vem.VolumeEventMapperOptions{}) + if err := mgr.Add(volumeEventMapper); err != nil { + return fmt.Errorf("error adding volume event mapper: %w", err) + } volumeEvents := irievent.NewGenerator(func(ctx context.Context) ([]*iri.Volume, error) { res, err := volumeRuntime.ListVolumes(ctx, &iri.ListVolumesRequest{}) diff --git a/poollet/volumepoollet/vem/vem.go b/poollet/volumepoollet/vem/vem.go new file mode 100644 index 000000000..737881936 --- /dev/null +++ b/poollet/volumepoollet/vem/vem.go @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package vem + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + "github.com/ironcore-dev/ironcore/iri/apis/volume" + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + "github.com/ironcore-dev/ironcore/poollet/volumepoollet/api/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" +) + +type VolumeEventMapper struct { + manager.Runnable + record.EventRecorder + client.Client + + volumeRuntime volume.RuntimeService + + relistPeriod time.Duration + lastFetched time.Time +} + +func (m *VolumeEventMapper) relist(ctx context.Context, log logr.Logger) error { + log.V(1).Info("Relisting volume cluster events") + toEventFilterTime := time.Now() + res, err := m.volumeRuntime.ListEvents(ctx, &iri.ListEventsRequest{ + Filter: &iri.EventFilter{EventsFromTime: m.lastFetched.Unix(), EventsToTime: toEventFilterTime.Unix()}, + }) + if err != nil { + return fmt.Errorf("error listing volume cluster events: %w", err) + } + + m.lastFetched = toEventFilterTime + for _, volumeEvent := range res.Events { + if volumeEvent.Spec.InvolvedObjectMeta.Labels != nil { + involvedVolume := &storagev1alpha1.Volume{ + ObjectMeta: metav1.ObjectMeta{ + UID: types.UID(volumeEvent.Spec.InvolvedObjectMeta.Labels[v1alpha1.VolumeUIDLabel]), + Name: volumeEvent.Spec.InvolvedObjectMeta.Labels[v1alpha1.VolumeNameLabel], + Namespace: volumeEvent.Spec.InvolvedObjectMeta.Labels[v1alpha1.VolumeNamespaceLabel], + }, + } + m.Eventf(involvedVolume, volumeEvent.Spec.Type, volumeEvent.Spec.Reason, volumeEvent.Spec.Message) + } + } + + return nil +} + +func (m *VolumeEventMapper) Start(ctx context.Context) error { + log := ctrl.LoggerFrom(ctx).WithName("mem") + m.lastFetched = time.Now() + wait.UntilWithContext(ctx, func(ctx context.Context) { + if err := m.relist(ctx, log); err != nil { + log.Error(err, "Error relisting") + } + }, m.relistPeriod) + return nil +} + +type VolumeEventMapperOptions struct { + RelistPeriod time.Duration +} + +func setVolumeEventMapperOptionsDefaults(o *VolumeEventMapperOptions) { + if o.RelistPeriod == 0 { + o.RelistPeriod = 1 * time.Minute + } +} + +func NewVolumeEventMapper(client client.Client, runtime volume.RuntimeService, recorder record.EventRecorder, opts VolumeEventMapperOptions) *VolumeEventMapper { + setVolumeEventMapperOptionsDefaults(&opts) + return &VolumeEventMapper{ + Client: client, + volumeRuntime: runtime, + relistPeriod: opts.RelistPeriod, + EventRecorder: recorder, + } +} diff --git a/poollet/volumepoollet/vem/vem_suite_test.go b/poollet/volumepoollet/vem/vem_suite_test.go new file mode 100644 index 000000000..59fca9f99 --- /dev/null +++ b/poollet/volumepoollet/vem/vem_suite_test.go @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package vem_test + +import ( + "testing" + "time" + + "github.com/ironcore-dev/controller-utils/buildutils" + "github.com/ironcore-dev/controller-utils/modutils" + corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1" + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + utilsenvtest "github.com/ironcore-dev/ironcore/utils/envtest" + "github.com/ironcore-dev/ironcore/utils/envtest/apiserver" + "github.com/ironcore-dev/ironcore/utils/envtest/controllermanager" + "github.com/ironcore-dev/ironcore/utils/envtest/process" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + . "sigs.k8s.io/controller-runtime/pkg/envtest/komega" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +var ( + cfg *rest.Config + testEnv *envtest.Environment + testEnvExt *utilsenvtest.EnvironmentExtensions + k8sClient client.Client +) + +const ( + eventuallyTimeout = 3 * time.Second + apiServiceTimeout = 5 * time.Minute + + controllerManagerService = "controller-manager" + + fooAnnotation = "foo" +) + +func TestControllers(t *testing.T) { + SetDefaultEventuallyTimeout(eventuallyTimeout) + + RegisterFailHandler(Fail) + RunSpecs(t, "Machine event mapper Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + var err error + By("bootstrapping test environment") + testEnv = &envtest.Environment{} + testEnvExt = &utilsenvtest.EnvironmentExtensions{ + APIServiceDirectoryPaths: []string{ + modutils.Dir("github.com/ironcore-dev/ironcore", "config", "apiserver", "apiservice", "bases"), + }, + ErrorIfAPIServicePathIsMissing: true, + AdditionalServices: []utilsenvtest.AdditionalService{ + { + Name: controllerManagerService, + }, + }, + } + + cfg, err = utilsenvtest.StartWithExtensions(testEnv, testEnvExt) + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + DeferCleanup(utilsenvtest.StopWithExtensions, testEnv, testEnvExt) + + Expect(storagev1alpha1.AddToScheme(scheme.Scheme)).To(Succeed()) + + // Init package-level k8sClient + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + SetClient(k8sClient) + + apiSrv, err := apiserver.New(cfg, apiserver.Options{ + MainPath: "github.com/ironcore-dev/ironcore/cmd/ironcore-apiserver", + BuildOptions: []buildutils.BuildOption{buildutils.ModModeMod}, + ETCDServers: []string{testEnv.ControlPlane.Etcd.URL.String()}, + Host: testEnvExt.APIServiceInstallOptions.LocalServingHost, + Port: testEnvExt.APIServiceInstallOptions.LocalServingPort, + CertDir: testEnvExt.APIServiceInstallOptions.LocalServingCertDir, + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(apiSrv.Start()).To(Succeed()) + DeferCleanup(apiSrv.Stop) + + Expect(utilsenvtest.WaitUntilAPIServicesReadyWithTimeout(apiServiceTimeout, testEnvExt, k8sClient, scheme.Scheme)).To(Succeed()) + + ctrlMgr, err := controllermanager.New(cfg, controllermanager.Options{ + Args: process.EmptyArgs().Set("controllers", "*"), + MainPath: "github.com/ironcore-dev/ironcore/cmd/ironcore-controller-manager", + BuildOptions: []buildutils.BuildOption{buildutils.ModModeMod}, + Host: testEnvExt.GetAdditionalServiceHost(controllerManagerService), + Port: testEnvExt.GetAdditionalServicePort(controllerManagerService), + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(ctrlMgr.Start()).To(Succeed()) + DeferCleanup(ctrlMgr.Stop) +}) + +func SetupTest() (*corev1.Namespace, *storagev1alpha1.VolumePool, *storagev1alpha1.VolumeClass) { + var ( + ns = &corev1.Namespace{} + vp = &storagev1alpha1.VolumePool{} + vc = &storagev1alpha1.VolumeClass{} + ) + + BeforeEach(func(ctx SpecContext) { + *ns = corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "test-ns-", + }, + } + Expect(k8sClient.Create(ctx, ns)).To(Succeed(), "failed to create test namespace") + DeferCleanup(k8sClient.Delete, ns) + + By("creating a volume pool") + *vp = storagev1alpha1.VolumePool{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "volumepool-", + Labels: map[string]string{ + "pool": "test-pool", + }, + }, + Spec: storagev1alpha1.VolumePoolSpec{ + ProviderID: "network-id", + }, + } + Expect(k8sClient.Create(ctx, vp)).To(Succeed()) + DeferCleanup(k8sClient.Delete, vp) + + *vc = storagev1alpha1.VolumeClass{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "volume-class-", + }, + Capabilities: corev1alpha1.ResourceList{ + corev1alpha1.ResourceIOPS: resource.MustParse("250Mi"), + corev1alpha1.ResourceTPS: resource.MustParse("1500"), + }, + } + Expect(k8sClient.Create(ctx, vc)).To(Succeed()) + DeferCleanup(k8sClient.Delete, vc) + }) + return ns, vp, vc +} diff --git a/poollet/volumepoollet/vem/vem_test.go b/poollet/volumepoollet/vem/vem_test.go new file mode 100644 index 000000000..096c42a67 --- /dev/null +++ b/poollet/volumepoollet/vem/vem_test.go @@ -0,0 +1,158 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors +// SPDX-License-Identifier: Apache-2.0 + +package vem_test + +import ( + "context" + "fmt" + "time" + + corev1alpha1 "github.com/ironcore-dev/ironcore/api/core/v1alpha1" + storagev1alpha1 "github.com/ironcore-dev/ironcore/api/storage/v1alpha1" + irievent "github.com/ironcore-dev/ironcore/iri/apis/event/v1alpha1" + "github.com/ironcore-dev/ironcore/iri/apis/meta/v1alpha1" + + iri "github.com/ironcore-dev/ironcore/iri/apis/volume/v1alpha1" + fakevolume "github.com/ironcore-dev/ironcore/iri/testing/volume" + "github.com/ironcore-dev/ironcore/poollet/volumepoollet/controllers" + "github.com/ironcore-dev/ironcore/poollet/volumepoollet/vcm" + "github.com/ironcore-dev/ironcore/poollet/volumepoollet/vem" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + metricserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" +) + +var _ = Describe("VolumeEventMapper", func() { + var srv = &fakevolume.FakeRuntimeService{} + ns, vp, vc := SetupTest() + + BeforeEach(func(ctx SpecContext) { + *srv = *fakevolume.NewFakeRuntimeService() + srv.SetVolumeClasses([]*fakevolume.FakeVolumeClassStatus{ + { + VolumeClassStatus: iri.VolumeClassStatus{ + VolumeClass: &iri.VolumeClass{ + Name: vc.Name, + Capabilities: &iri.VolumeClassCapabilities{ + Tps: vc.Capabilities.TPS().Value(), + Iops: vc.Capabilities.IOPS().Value(), + }, + }, + }, + }, + }) + + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + Metrics: metricserver.Options{ + BindAddress: "0", + }, + }) + Expect(err).ToNot(HaveOccurred()) + + volumeClassMapper := vcm.NewGeneric(srv, vcm.GenericOptions{ + RelistPeriod: 2 * time.Second, + }) + Expect(k8sManager.Add(volumeClassMapper)).To(Succeed()) + + volumeEventMapper := vem.NewVolumeEventMapper(k8sManager.GetClient(), srv, k8sManager.GetEventRecorderFor("test"), vem.VolumeEventMapperOptions{ + RelistPeriod: 2 * time.Second, + }) + Expect(k8sManager.Add(volumeEventMapper)).To(Succeed()) + + Expect((&controllers.VolumeReconciler{ + EventRecorder: &record.FakeRecorder{}, + Client: k8sManager.GetClient(), + VolumeRuntime: srv, + VolumeClassMapper: volumeClassMapper, + VolumePoolName: vp.Name, + }).SetupWithManager(k8sManager)).To(Succeed()) + + mgrCtx, cancel := context.WithCancel(context.Background()) + DeferCleanup(cancel) + + go func() { + defer GinkgoRecover() + Expect(k8sManager.Start(mgrCtx)).To(Succeed(), "failed to start manager") + }() + }) + + It("should get event list for volume", func(ctx SpecContext) { + By("creating a volume") + const fooAnnotationValue = "bar" + volume := &storagev1alpha1.Volume{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns.Name, + GenerateName: "volume-", + Annotations: map[string]string{ + fooAnnotation: fooAnnotationValue, + }, + }, + Spec: storagev1alpha1.VolumeSpec{ + Resources: corev1alpha1.ResourceList{ + corev1alpha1.ResourceStorage: resource.MustParse("250"), + }, + VolumeClassRef: &corev1.LocalObjectReference{Name: vc.Name}, + VolumePoolRef: &corev1.LocalObjectReference{Name: vp.Name}, + }, + } + Expect(k8sClient.Create(ctx, volume)).To(Succeed(), "failed to create volume") + + By("waiting for the runtime to report the volume") + Eventually(srv).Should(SatisfyAll( + HaveField("Volumes", HaveLen(1)), + )) + _, iriVolume := GetSingleMapEntry(srv.Volumes) + + By("setting an event for iri volume") + eventList := []*fakevolume.FakeEvent{{ + Event: irievent.Event{ + Spec: &irievent.EventSpec{ + InvolvedObjectMeta: &v1alpha1.ObjectMetadata{ + Labels: iriVolume.Metadata.Labels, + }, + Reason: "testing", + Message: "this is test event", + Type: "Normal", + EventTime: time.Now().Unix(), + }}, + }, + } + srv.SetEvents(eventList) + + By("validating event has been emitted for correct volume") + volumeEventList := &corev1.EventList{} + selectorField := fields.Set{} + selectorField["involvedObject.name"] = volume.GetName() + Eventually(func(g Gomega) []corev1.Event { + err := k8sClient.List(ctx, volumeEventList, + client.InNamespace(ns.Name), client.MatchingFieldsSelector{Selector: selectorField.AsSelector()}, + ) + g.Expect(err).NotTo(HaveOccurred()) + return volumeEventList.Items + }).Should(ContainElement(SatisfyAll( + HaveField("Reason", Equal("testing")), + HaveField("Message", Equal("this is test event")), + HaveField("Type", Equal(corev1.EventTypeNormal)), + ))) + }) +}) + +func GetSingleMapEntry[K comparable, V any](m map[K]V) (K, V) { + if n := len(m); n != 1 { + Fail(fmt.Sprintf("Expected for map to have a single entry but got %d", n), 1) + } + for k, v := range m { + return k, v + } + panic("unreachable") +}