From 75401bf3ee61cd0c55e9495d279389e51a33aace Mon Sep 17 00:00:00 2001 From: Alina Militaru <41362174+asincu@users.noreply.github.com> Date: Tue, 1 Jun 2021 09:28:10 -0700 Subject: [PATCH] [SAAS-1407] Update PacketCapture with a status subresource (#486) * [SAAS-1407] Update PacketCapture with a status subresource * [CODE REVIEW] Use named values instead of string as key * [CODE REVIEW] Add directory and more comments Co-authored-by: Alina Militaru --- .../crd.projectcalico.org_packetcaptures.yaml | 32 ++++++++ .../v1/packetcapture_types.go | 3 +- lib/apis/v3/openapi_generated.go | 81 ++++++++++++++++++- lib/apis/v3/packetcapture.go | 23 ++++++ lib/apis/v3/zz_generated.deepcopy.go | 45 +++++++++++ lib/clientv3/packetcapture_e2e_test.go | 31 ++++++- 6 files changed, 212 insertions(+), 3 deletions(-) diff --git a/config/crd/crd.projectcalico.org_packetcaptures.yaml b/config/crd/crd.projectcalico.org_packetcaptures.yaml index ae1ac0551..fc1091cf7 100644 --- a/config/crd/crd.projectcalico.org_packetcaptures.yaml +++ b/config/crd/crd.projectcalico.org_packetcaptures.yaml @@ -56,6 +56,38 @@ spec: \"dev\" \t! has(label_name)" type: string type: object + status: + description: PacketCaptureStatus describes the files that have been captured, + for a given PacketCapture, on each node that generates packet capture + files + properties: + files: + items: + description: PacketCaptureFile describes files generated by a PacketCapture. + It describes the location of the packet capture files that is + identified via a node, its directory and the file names generated. + properties: + directory: + description: Directory represents the path inside the calico-node + container for the the generated files + type: string + fileNames: + description: 'FileNames represents the name of the generated + file for a PacketCapture ordered alphanumerically. The active + packet capture file will be identified using the following + schema: "{workload endpoint name}_{host network interface}.pcap" + . Rotated capture files name will contain an index matching + the rotation timestamp.' + items: + type: string + type: array + node: + description: Node identifies with a a physical node from the + cluster via its hostname + type: string + type: object + type: array + type: object type: object served: true storage: true diff --git a/lib/apis/crd.projectcalico.org/v1/packetcapture_types.go b/lib/apis/crd.projectcalico.org/v1/packetcapture_types.go index ec7d80eb1..743543cac 100644 --- a/lib/apis/crd.projectcalico.org/v1/packetcapture_types.go +++ b/lib/apis/crd.projectcalico.org/v1/packetcapture_types.go @@ -15,5 +15,6 @@ import ( type PacketCapture struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec v3.PacketCaptureSpec `json:"spec,omitempty"` + Spec v3.PacketCaptureSpec `json:"spec,omitempty"` + Status v3.PacketCaptureStatus `json:"status,omitempty"` } diff --git a/lib/apis/v3/openapi_generated.go b/lib/apis/v3/openapi_generated.go index 19d43351a..78e52e860 100644 --- a/lib/apis/v3/openapi_generated.go +++ b/lib/apis/v3/openapi_generated.go @@ -206,8 +206,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/projectcalico/libcalico-go/lib/apis/v3.NodeWireguardSpec": schema_libcalico_go_lib_apis_v3_NodeWireguardSpec(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.OrchRef": schema_libcalico_go_lib_apis_v3_OrchRef(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCapture": schema_libcalico_go_lib_apis_v3_PacketCapture(ref), + "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureFile": schema_libcalico_go_lib_apis_v3_PacketCaptureFile(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureList": schema_libcalico_go_lib_apis_v3_PacketCaptureList(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureSpec": schema_libcalico_go_lib_apis_v3_PacketCaptureSpec(ref), + "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureStatus": schema_libcalico_go_lib_apis_v3_PacketCaptureStatus(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.PolicyControllerConfig": schema_libcalico_go_lib_apis_v3_PolicyControllerConfig(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.PrefixAdvertisement": schema_libcalico_go_lib_apis_v3_PrefixAdvertisement(ref), "github.com/projectcalico/libcalico-go/lib/apis/v3.Profile": schema_libcalico_go_lib_apis_v3_Profile(ref), @@ -9618,11 +9620,60 @@ func schema_libcalico_go_lib_apis_v3_PacketCapture(ref common.ReferenceCallback) Ref: ref("github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureSpec"), }, }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Status of the PacketCapture", + Default: map[string]interface{}{}, + Ref: ref("github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureStatus"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureSpec", "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_libcalico_go_lib_apis_v3_PacketCaptureFile(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacketCaptureFile describes files generated by a PacketCapture. It describes the location of the packet capture files that is identified via a node, its directory and the file names generated.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "node": { + SchemaProps: spec.SchemaProps{ + Description: "Node identifies with a a physical node from the cluster via its hostname", + Type: []string{"string"}, + Format: "", + }, + }, + "directory": { + SchemaProps: spec.SchemaProps{ + Description: "Directory represents the path inside the calico-node container for the the generated files", + Type: []string{"string"}, + Format: "", + }, + }, + "fileNames": { + SchemaProps: spec.SchemaProps{ + Description: "FileNames represents the name of the generated file for a PacketCapture ordered alphanumerically. The active packet capture file will be identified using the following schema: \"{workload endpoint name}_{host network interface}.pcap\" . Rotated capture files name will contain an index matching the rotation timestamp.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, } } @@ -9695,6 +9746,34 @@ func schema_libcalico_go_lib_apis_v3_PacketCaptureSpec(ref common.ReferenceCallb } } +func schema_libcalico_go_lib_apis_v3_PacketCaptureStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "PacketCaptureStatus describes the files that have been captured, for a given PacketCapture, on each node that generates packet capture files", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "files": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureFile"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/projectcalico/libcalico-go/lib/apis/v3.PacketCaptureFile"}, + } +} + func schema_libcalico_go_lib_apis_v3_PolicyControllerConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/lib/apis/v3/packetcapture.go b/lib/apis/v3/packetcapture.go index 2f2830cf1..f42d71acd 100644 --- a/lib/apis/v3/packetcapture.go +++ b/lib/apis/v3/packetcapture.go @@ -13,6 +13,7 @@ const ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:subresource:status // PacketCapture contains the configuration for any packet capture. type PacketCapture struct { @@ -21,6 +22,8 @@ type PacketCapture struct { metav1.ObjectMeta `json:"metadata,omitempty"` // Specification of the PacketCapture. Spec PacketCaptureSpec `json:"spec,omitempty"` + // Status of the PacketCapture + Status PacketCaptureStatus `json:"status,omitempty"` } // PacketCaptureSpec contains the values of the packet capture. @@ -54,6 +57,26 @@ type PacketCaptureSpec struct { Selector string `json:"selector,omitempty" validate:"selector"` } +// PacketCaptureStatus describes the files that have been captured, for a given PacketCapture, on each node +// that generates packet capture files +type PacketCaptureStatus struct { + Files []PacketCaptureFile `json:"files,omitempty"` +} + +// PacketCaptureFile describes files generated by a PacketCapture. It describes the location of the packet capture files +// that is identified via a node, its directory and the file names generated. +type PacketCaptureFile struct { + // Node identifies with a a physical node from the cluster via its hostname + Node string `json:"node,omitempty"` + // Directory represents the path inside the calico-node container for the the generated files + Directory string `json:"directory,omitempty"` + // FileNames represents the name of the generated file for a PacketCapture ordered alphanumerically. + // The active packet capture file will be identified using the following schema: + // "{workload endpoint name}_{host network interface}.pcap" . + // Rotated capture files name will contain an index matching the rotation timestamp. + FileNames []string `json:"fileNames,omitempty"` +} + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // PacketCaptureList contains a list of PacketCapture resources. diff --git a/lib/apis/v3/zz_generated.deepcopy.go b/lib/apis/v3/zz_generated.deepcopy.go index cf5fe53ce..74c1efc15 100644 --- a/lib/apis/v3/zz_generated.deepcopy.go +++ b/lib/apis/v3/zz_generated.deepcopy.go @@ -4092,6 +4092,7 @@ func (in *PacketCapture) DeepCopyInto(out *PacketCapture) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) return } @@ -4113,6 +4114,27 @@ func (in *PacketCapture) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacketCaptureFile) DeepCopyInto(out *PacketCaptureFile) { + *out = *in + if in.FileNames != nil { + in, out := &in.FileNames, &out.FileNames + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacketCaptureFile. +func (in *PacketCaptureFile) DeepCopy() *PacketCaptureFile { + if in == nil { + return nil + } + out := new(PacketCaptureFile) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PacketCaptureList) DeepCopyInto(out *PacketCaptureList) { *out = *in @@ -4162,6 +4184,29 @@ func (in *PacketCaptureSpec) DeepCopy() *PacketCaptureSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PacketCaptureStatus) DeepCopyInto(out *PacketCaptureStatus) { + *out = *in + if in.Files != nil { + in, out := &in.Files, &out.Files + *out = make([]PacketCaptureFile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PacketCaptureStatus. +func (in *PacketCaptureStatus) DeepCopy() *PacketCaptureStatus { + if in == nil { + return nil + } + out := new(PacketCaptureStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PolicyControllerConfig) DeepCopyInto(out *PolicyControllerConfig) { *out = *in diff --git a/lib/clientv3/packetcapture_e2e_test.go b/lib/clientv3/packetcapture_e2e_test.go index c6aac425b..8a31f59a1 100644 --- a/lib/clientv3/packetcapture_e2e_test.go +++ b/lib/clientv3/packetcapture_e2e_test.go @@ -6,6 +6,8 @@ import ( "context" "time" + log "github.com/sirupsen/logrus" + . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" @@ -189,6 +191,33 @@ var _ = testutils.E2eDatastoreDescribe("PacketCapture tests", testutils.Datastor testutils.Resource(apiv3.KindPacketCapture, namespace2, name2, spec2), )) + By("Setting status1 on resource") + status1 := apiv3.PacketCaptureStatus{ + Files: []apiv3.PacketCaptureFile{ + { + Node: "node1", + FileNames: []string{"file1", "file2"}, + }, + }, + } + res.Status = status1 + log.Infof("Before update %#v", res) + res, outError = c.PacketCaptures().Update(ctx, res, options.SetOptions{}) + Expect(outError).ToNot(HaveOccurred()) + log.Infof("After update %#v", res) + Expect(res.Status.Files).To(HaveLen(1)) + Expect(res).To(MatchResourceWithStatus(apiv3.KindPacketCapture, namespace1, name1, spec2, status1)) + + By("Getting resource and verifying status1 is present") + res, outError = c.PacketCaptures().Get(ctx, namespace1, name1, options.GetOptions{}) + log.Infof("After get %#v", res) + Expect(outError).ToNot(HaveOccurred()) + Expect(res).To(MatchResourceWithStatus(apiv3.KindPacketCapture, namespace1, name1, spec2, status1)) + Expect(res.Status.Files).To(HaveLen(1)) + + // Track the version of the updated name1 data. + rv1_3 := res.ResourceVersion + if config.Spec.DatastoreType != apiconfig.Kubernetes { By("Deleting PacketCapture (namespace1/name1) with the old resource version") _, outError = c.PacketCaptures().Delete(ctx, namespace1, name1, options.DeleteOptions{ResourceVersion: rv1_1}) @@ -197,7 +226,7 @@ var _ = testutils.E2eDatastoreDescribe("PacketCapture tests", testutils.Datastor } By("Deleting PacketCapture (namespace1/name1) with the new resource version") - dres, outError := c.PacketCaptures().Delete(ctx, namespace1, name1, options.DeleteOptions{ResourceVersion: rv1_2}) + dres, outError := c.PacketCaptures().Delete(ctx, namespace1, name1, options.DeleteOptions{ResourceVersion: rv1_3}) Expect(outError).NotTo(HaveOccurred()) Expect(dres).To(MatchResource(apiv3.KindPacketCapture, namespace1, name1, spec2))