From 9db79843073e679f73b847785d5c6a738abfdcfb Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 12 Mar 2021 16:01:22 +0100 Subject: [PATCH 01/20] Add permissions for PodNetworkConnectivityChecks --- manifests/03-clusterrole.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/manifests/03-clusterrole.yaml b/manifests/03-clusterrole.yaml index 1f10b7b6e..7c2b193b0 100644 --- a/manifests/03-clusterrole.yaml +++ b/manifests/03-clusterrole.yaml @@ -187,7 +187,15 @@ rules: verbs: - get - list - - watch + - watch +- apiGroups: + - controlplane.operator.openshift.io + resources: + - podnetworkconnectivitychecks + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 From dea368aedc582b89e214153b828cbdfb7d0fc806 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Mon, 15 Mar 2021 13:08:07 +0100 Subject: [PATCH 02/20] Gather PodNetworkConnectivityChecks --- pkg/gather/clusterconfig/0_gatherer.go | 1 + pkg/gather/clusterconfig/const.go | 3 ++ .../pod_network_connectivity_checks.go | 38 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 pkg/gather/clusterconfig/pod_network_connectivity_checks.go diff --git a/pkg/gather/clusterconfig/0_gatherer.go b/pkg/gather/clusterconfig/0_gatherer.go index f94746d66..bff5e51cc 100644 --- a/pkg/gather/clusterconfig/0_gatherer.go +++ b/pkg/gather/clusterconfig/0_gatherer.go @@ -104,6 +104,7 @@ var gatherFunctions = map[string]gathering{ "sap_pods": failable(GatherSAPPods), "sap_datahubs": failable(GatherSAPDatahubs), "olm_operators": failable(GatherOLMOperators), + "pod_network_connectivity_checks": important(GatherPNCC), } // New creates new Gatherer diff --git a/pkg/gather/clusterconfig/const.go b/pkg/gather/clusterconfig/const.go index 5fabeba35..90d7d11ec 100644 --- a/pkg/gather/clusterconfig/const.go +++ b/pkg/gather/clusterconfig/const.go @@ -24,6 +24,9 @@ var ( datahubGroupVersionResource = schema.GroupVersionResource{ Group: "installers.datahub.sap.com", Version: "v1alpha1", Resource: "datahubs", } + pnccGroupVersionResource = schema.GroupVersionResource{ + Group: "controlplane.operator.openshift.io", Version: "v1alpha1", Resource: "podnetworkconnectivitychecks", + } ) func init() { diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go new file mode 100644 index 000000000..358a10c82 --- /dev/null +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -0,0 +1,38 @@ +package clusterconfig + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + + "github.com/openshift/insights-operator/pkg/record" +) + +// GatherPNCC collects PodNetworkConnectivityChecks. +func GatherPNCC(g *Gatherer, c chan<- gatherResult) { + gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) + if err != nil { + c <- gatherResult{errors: []error{err}} + return + } + gatherKubeClient, err := kubernetes.NewForConfig(g.gatherProtoKubeConfig) + if err != nil { + c <- gatherResult{errors: []error{err}} + return + } + + records, errors := gatherPNCC(g.ctx, gatherDynamicClient, gatherKubeClient.CoreV1()) + c <- gatherResult{records: records, errors: errors} +} + +func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient corev1client.CoreV1Interface) ([]record.Record, []error) { + pnccList, err := dynamicClient.Resource(pnccGroupVersionResource).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, []error{err} + } + + return []record.Record{{Name: "config/aaaaaaaaa", Item: record.JSONMarshaller{Object: pnccList}}}, nil +} From 2f88e3d5101e9dc7110f73b7ecd1ff8e17a92a8e Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Tue, 16 Mar 2021 20:24:35 +0100 Subject: [PATCH 03/20] Fix PNCC name --- pkg/gather/clusterconfig/pod_network_connectivity_checks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 358a10c82..4326d5e93 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -34,5 +34,5 @@ func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient return nil, []error{err} } - return []record.Record{{Name: "config/aaaaaaaaa", Item: record.JSONMarshaller{Object: pnccList}}}, nil + return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: pnccList}}}, nil } From c6dace65c619bd70d46da5879c8c6cfea67687ea Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Tue, 6 Apr 2021 10:30:51 +0200 Subject: [PATCH 04/20] Gather PNCC info --- .../pod_network_connectivity_checks.go | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 4326d5e93..345bc5f8f 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -2,7 +2,9 @@ package clusterconfig import ( "context" + "encoding/json" + controlplanev1 "github.com/openshift/api/operatorcontrolplane/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" @@ -28,11 +30,50 @@ func GatherPNCC(g *Gatherer, c chan<- gatherResult) { c <- gatherResult{records: records, errors: errors} } +func getUnsuccessfulChecks(entries []controlplanev1.LogEntry) []controlplanev1.LogEntry { + unsuccesseful := []controlplanev1.LogEntry{} + for _, entry := range entries { + if !entry.Success { + unsuccesseful = append(unsuccesseful, entry) + } + } + return unsuccesseful +} + func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient corev1client.CoreV1Interface) ([]record.Record, []error) { - pnccList, err := dynamicClient.Resource(pnccGroupVersionResource).List(ctx, metav1.ListOptions{}) + pnccListUnstruct, err := dynamicClient.Resource(pnccGroupVersionResource).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, []error{err} + } + + jsonBytes, err := pnccListUnstruct.MarshalJSON() if err != nil { return nil, []error{err} } - return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: pnccList}}}, nil + pnccListStruct := controlplanev1.PodNetworkConnectivityCheckList{} + if err := json.Unmarshal(jsonBytes, &pnccListStruct); err != nil { + return nil, []error{err} + } + + unsuccessful := []controlplanev1.LogEntry{} + for _, pncc := range pnccListStruct.Items { + unsuccessful = append(unsuccessful, getUnsuccessfulChecks(pncc.Status.Failures)...) + for _, outage := range pncc.Status.Outages { + unsuccessful = append(unsuccessful, getUnsuccessfulChecks(outage.StartLogs)...) + unsuccessful = append(unsuccessful, getUnsuccessfulChecks(outage.EndLogs)...) + } + } + + msg := map[string]struct{}{} + reason := map[string]struct{}{} + for _, entry := range unsuccessful { + msg[entry.Message] = struct{}{} + reason[entry.Reason] = struct{}{} + } + + return []record.Record{{Name: "config/podnetworkconnectivitychecks_unstruct", Item: record.JSONMarshaller{Object: pnccListUnstruct}}, + {Name: "config/podnetworkconnectivitychecks_struct", Item: record.JSONMarshaller{Object: pnccListStruct}}, + {Name: "config/podnetworkconnectivitychecks_msg", Item: record.JSONMarshaller{Object: msg}}, + {Name: "config/podnetworkconnectivitychecks_reason", Item: record.JSONMarshaller{Object: reason}}}, nil } From 71a197265bb85a5bb2e429e21cc24e2c12f3162a Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Tue, 6 Apr 2021 10:31:04 +0200 Subject: [PATCH 05/20] Vendor PNCC struct --- ...10-pod-network-connectivity-check.crd.yaml | 259 ++++++++++++++++++ .../api/operatorcontrolplane/v1alpha1/doc.go | 7 + .../operatorcontrolplane/v1alpha1/register.go | 39 +++ .../v1alpha1/types_conditioncheck.go | 181 ++++++++++++ .../v1alpha1/zz_generated.deepcopy.go | 198 +++++++++++++ .../zz_generated.swagger_doc_generated.go | 93 +++++++ vendor/modules.txt | 1 + 7 files changed, 778 insertions(+) create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/0000_10-pod-network-connectivity-check.crd.yaml create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/0000_10-pod-network-connectivity-check.crd.yaml b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/0000_10-pod-network-connectivity-check.crd.yaml new file mode 100644 index 000000000..1009c9162 --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/0000_10-pod-network-connectivity-check.crd.yaml @@ -0,0 +1,259 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + include.release.openshift.io/self-managed-high-availability: "true" + include.release.openshift.io/single-node-developer: "true" + name: podnetworkconnectivitychecks.controlplane.operator.openshift.io +spec: + group: controlplane.operator.openshift.io + names: + kind: PodNetworkConnectivityCheck + listKind: PodNetworkConnectivityCheckList + plural: podnetworkconnectivitychecks + singular: podnetworkconnectivitycheck + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: PodNetworkConnectivityCheck + type: object + required: + - spec + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the source and target of the connectivity check + type: object + required: + - sourcePod + - targetEndpoint + properties: + sourcePod: + description: SourcePod names the pod from which the condition will + be checked + type: string + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + targetEndpoint: + description: EndpointAddress to check. A TCP address of the form host:port. + Note that if host is a DNS name, then the check would fail if the + DNS name cannot be resolved. Specify an IP address for host to bypass + DNS name lookup. + type: string + pattern: ^\S+:\d*$ + tlsClientCert: + description: TLSClientCert, if specified, references a kubernetes.io/tls + type secret with 'tls.crt' and 'tls.key' entries containing an optional + TLS client certificate and key to be used when checking endpoints + that require a client certificate in order to gracefully preform + the scan without causing excessive logging in the endpoint process. + The secret must exist in the same namespace as this resource. + type: object + required: + - name + properties: + name: + description: name is the metadata.name of the referenced secret + type: string + status: + description: Status contains the observed status of the connectivity check + type: object + properties: + conditions: + description: Conditions summarize the status of the check + type: array + items: + description: PodNetworkConnectivityCheckCondition represents the + overall status of the pod network connectivity. + type: object + required: + - lastTransitionTime + - status + - type + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + type: string + format: date-time + nullable: true + message: + description: Message indicating details about last transition + in a human readable format. + type: string + reason: + description: Reason for the condition's last status transition + in a machine readable format. + type: string + status: + description: Status of the condition + type: string + type: + description: Type of the condition + type: string + failures: + description: Failures contains logs of unsuccessful check actions + type: array + items: + description: LogEntry records events + type: object + required: + - success + - time + properties: + latency: + description: Latency records how long the action mentioned in + the entry took. + type: string + nullable: true + message: + description: Message explaining status in a human readable format. + type: string + reason: + description: Reason for status in a machine readable format. + type: string + success: + description: Success indicates if the log entry indicates a + success or failure. + type: boolean + time: + description: Start time of check action. + type: string + format: date-time + nullable: true + outages: + description: Outages contains logs of time periods of outages + type: array + items: + description: OutageEntry records time period of an outage + type: object + required: + - start + properties: + end: + description: End of outage detected + type: string + format: date-time + nullable: true + endLogs: + description: EndLogs contains log entries related to the end + of this outage. Should contain the success entry that resolved + the outage and possibly a few of the failure log entries that + preceded it. + type: array + items: + description: LogEntry records events + type: object + required: + - success + - time + properties: + latency: + description: Latency records how long the action mentioned + in the entry took. + type: string + nullable: true + message: + description: Message explaining status in a human readable + format. + type: string + reason: + description: Reason for status in a machine readable format. + type: string + success: + description: Success indicates if the log entry indicates + a success or failure. + type: boolean + time: + description: Start time of check action. + type: string + format: date-time + nullable: true + message: + description: Message summarizes outage details in a human readable + format. + type: string + start: + description: Start of outage detected + type: string + format: date-time + nullable: true + startLogs: + description: StartLogs contains log entries related to the start + of this outage. Should contain the original failure, any entries + where the failure mode changed. + type: array + items: + description: LogEntry records events + type: object + required: + - success + - time + properties: + latency: + description: Latency records how long the action mentioned + in the entry took. + type: string + nullable: true + message: + description: Message explaining status in a human readable + format. + type: string + reason: + description: Reason for status in a machine readable format. + type: string + success: + description: Success indicates if the log entry indicates + a success or failure. + type: boolean + time: + description: Start time of check action. + type: string + format: date-time + nullable: true + successes: + description: Successes contains logs successful check actions + type: array + items: + description: LogEntry records events + type: object + required: + - success + - time + properties: + latency: + description: Latency records how long the action mentioned in + the entry took. + type: string + nullable: true + message: + description: Message explaining status in a human readable format. + type: string + reason: + description: Reason for status in a machine readable format. + type: string + success: + description: Success indicates if the log entry indicates a + success or failure. + type: boolean + time: + description: Start time of check action. + type: string + format: date-time + nullable: true diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go new file mode 100644 index 000000000..9af889921 --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go @@ -0,0 +1,7 @@ +// +k8s:deepcopy-gen=package,register +// +k8s:defaulter-gen=TypeMeta +// +k8s:openapi-gen=true + +// +kubebuilder:validation:Optional +// +groupName=controlplane.operator.openshift.io +package v1alpha1 diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go new file mode 100644 index 000000000..1ffc55381 --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go @@ -0,0 +1,39 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + GroupName = "controlplane.operator.openshift.io" + GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // Install is a function which adds this version to a scheme + Install = schemeBuilder.AddToScheme + + // SchemeGroupVersion generated code relies on this name + // Deprecated + SchemeGroupVersion = GroupVersion + // AddToScheme exists solely to keep the old generators creating valid code + // DEPRECATED + AddToScheme = schemeBuilder.AddToScheme +) + +// Resource generated code relies on this being here, but it logically belongs to the group +// DEPRECATED +func Resource(resource string) schema.GroupResource { + return schema.GroupResource{Group: GroupName, Resource: resource} +} + +func addKnownTypes(scheme *runtime.Scheme) error { + metav1.AddToGroupVersion(scheme, GroupVersion) + + scheme.AddKnownTypes(GroupVersion, + &PodNetworkConnectivityCheck{}, + &PodNetworkConnectivityCheckList{}, + ) + + return nil +} diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go new file mode 100644 index 000000000..61af620cb --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go @@ -0,0 +1,181 @@ +// Package v1alpha1 is an API version in the controlplane.operator.openshift.io group +package v1alpha1 + +import ( + v1 "github.com/openshift/api/config/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodNetworkConnectivityCheck +// +kubebuilder:subresource:status +type PodNetworkConnectivityCheck struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata"` + + // Spec defines the source and target of the connectivity check + // +kubebuilder:validation:Required + // +required + Spec PodNetworkConnectivityCheckSpec `json:"spec"` + + // Status contains the observed status of the connectivity check + // +optional + Status PodNetworkConnectivityCheckStatus `json:"status,omitempty"` +} + +type PodNetworkConnectivityCheckSpec struct { + // SourcePod names the pod from which the condition will be checked + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$` + // +required + SourcePod string `json:"sourcePod"` + + // EndpointAddress to check. A TCP address of the form host:port. Note that + // if host is a DNS name, then the check would fail if the DNS name cannot + // be resolved. Specify an IP address for host to bypass DNS name lookup. + // +kubebuilder:validation:Required + // +kubebuilder:validation:Pattern=`^\S+:\d*$` + // +required + TargetEndpoint string `json:"targetEndpoint"` + + // TLSClientCert, if specified, references a kubernetes.io/tls type secret with 'tls.crt' and + // 'tls.key' entries containing an optional TLS client certificate and key to be used when + // checking endpoints that require a client certificate in order to gracefully preform the + // scan without causing excessive logging in the endpoint process. The secret must exist in + // the same namespace as this resource. + // +optional + TLSClientCert v1.SecretNameReference `json:"tlsClientCert,omitempty"` +} + +// +k8s:deepcopy-gen=true +type PodNetworkConnectivityCheckStatus struct { + // Successes contains logs successful check actions + // +optional + Successes []LogEntry `json:"successes,omitempty"` + + // Failures contains logs of unsuccessful check actions + // +optional + Failures []LogEntry `json:"failures,omitempty"` + + // Outages contains logs of time periods of outages + // +optional + Outages []OutageEntry `json:"outages,omitempty"` + + // Conditions summarize the status of the check + // +patchMergeKey=type + // +patchStrategy=merge + // +optional + Conditions []PodNetworkConnectivityCheckCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +// LogEntry records events +type LogEntry struct { + // Start time of check action. + // +kubebuilder:validation:Required + // +required + // +nullable + Start metav1.Time `json:"time"` + + // Success indicates if the log entry indicates a success or failure. + // +kubebuilder:validation:Required + // +required + Success bool `json:"success"` + + // Reason for status in a machine readable format. + // +optional + Reason string `json:"reason,omitempty"` + + // Message explaining status in a human readable format. + // +optional + Message string `json:"message,omitempty"` + + // Latency records how long the action mentioned in the entry took. + // +optional + // +nullable + Latency metav1.Duration `json:"latency,omitempty"` +} + +// OutageEntry records time period of an outage +type OutageEntry struct { + + // Start of outage detected + // +kubebuilder:validation:Required + // +required + // +nullable + Start metav1.Time `json:"start"` + + // End of outage detected + // +optional + // +nullable + End metav1.Time `json:"end,omitempty"` + + // StartLogs contains log entries related to the start of this outage. Should contain + // the original failure, any entries where the failure mode changed. + // +optional + StartLogs []LogEntry `json:"startLogs,omitempty"` + + // EndLogs contains log entries related to the end of this outage. Should contain the success + // entry that resolved the outage and possibly a few of the failure log entries that preceded it. + // +optional + EndLogs []LogEntry `json:"endLogs,omitempty"` + + // Message summarizes outage details in a human readable format. + // +optional + Message string `json:"message,omitempty"` +} + +// PodNetworkConnectivityCheckCondition represents the overall status of the pod network connectivity. +// +k8s:deepcopy-gen=true +type PodNetworkConnectivityCheckCondition struct { + + // Type of the condition + // +kubebuilder:validation:Required + // +required + Type PodNetworkConnectivityCheckConditionType `json:"type"` + + // Status of the condition + // +kubebuilder:validation:Required + // +required + Status metav1.ConditionStatus `json:"status"` + + // Reason for the condition's last status transition in a machine readable format. + // +optional + Reason string `json:"reason,omitempty"` + + // Message indicating details about last transition in a human readable format. + // +optional + Message string `json:"message,omitempty"` + + // Last time the condition transitioned from one status to another. + // +kubebuilder:validation:Required + // +required + // +nullable + LastTransitionTime metav1.Time `json:"lastTransitionTime"` +} + +const ( + LogEntryReasonDNSResolve = "DNSResolve" + LogEntryReasonDNSError = "DNSError" + LogEntryReasonTCPConnect = "TCPConnect" + LogEntryReasonTCPConnectError = "TCPConnectError" +) + +type PodNetworkConnectivityCheckConditionType string + +const ( + // Reachable indicates that the endpoint was reachable from the pod. + Reachable PodNetworkConnectivityCheckConditionType = "Reachable" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodNetworkConnectivityCheckList is a collection of PodNetworkConnectivityCheck +type PodNetworkConnectivityCheckList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + // Items contains the items + Items []PodNetworkConnectivityCheck `json:"items"` +} diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..2f123c4ca --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,198 @@ +// +build !ignore_autogenerated + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LogEntry) DeepCopyInto(out *LogEntry) { + *out = *in + in.Start.DeepCopyInto(&out.Start) + out.Latency = in.Latency + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LogEntry. +func (in *LogEntry) DeepCopy() *LogEntry { + if in == nil { + return nil + } + out := new(LogEntry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OutageEntry) DeepCopyInto(out *OutageEntry) { + *out = *in + in.Start.DeepCopyInto(&out.Start) + in.End.DeepCopyInto(&out.End) + if in.StartLogs != nil { + in, out := &in.StartLogs, &out.StartLogs + *out = make([]LogEntry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EndLogs != nil { + in, out := &in.EndLogs, &out.EndLogs + *out = make([]LogEntry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OutageEntry. +func (in *OutageEntry) DeepCopy() *OutageEntry { + if in == nil { + return nil + } + out := new(OutageEntry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodNetworkConnectivityCheck) DeepCopyInto(out *PodNetworkConnectivityCheck) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodNetworkConnectivityCheck. +func (in *PodNetworkConnectivityCheck) DeepCopy() *PodNetworkConnectivityCheck { + if in == nil { + return nil + } + out := new(PodNetworkConnectivityCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodNetworkConnectivityCheck) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodNetworkConnectivityCheckCondition) DeepCopyInto(out *PodNetworkConnectivityCheckCondition) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodNetworkConnectivityCheckCondition. +func (in *PodNetworkConnectivityCheckCondition) DeepCopy() *PodNetworkConnectivityCheckCondition { + if in == nil { + return nil + } + out := new(PodNetworkConnectivityCheckCondition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodNetworkConnectivityCheckList) DeepCopyInto(out *PodNetworkConnectivityCheckList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PodNetworkConnectivityCheck, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodNetworkConnectivityCheckList. +func (in *PodNetworkConnectivityCheckList) DeepCopy() *PodNetworkConnectivityCheckList { + if in == nil { + return nil + } + out := new(PodNetworkConnectivityCheckList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodNetworkConnectivityCheckList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodNetworkConnectivityCheckSpec) DeepCopyInto(out *PodNetworkConnectivityCheckSpec) { + *out = *in + out.TLSClientCert = in.TLSClientCert + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodNetworkConnectivityCheckSpec. +func (in *PodNetworkConnectivityCheckSpec) DeepCopy() *PodNetworkConnectivityCheckSpec { + if in == nil { + return nil + } + out := new(PodNetworkConnectivityCheckSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodNetworkConnectivityCheckStatus) DeepCopyInto(out *PodNetworkConnectivityCheckStatus) { + *out = *in + if in.Successes != nil { + in, out := &in.Successes, &out.Successes + *out = make([]LogEntry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Failures != nil { + in, out := &in.Failures, &out.Failures + *out = make([]LogEntry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Outages != nil { + in, out := &in.Outages, &out.Outages + *out = make([]OutageEntry, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]PodNetworkConnectivityCheckCondition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodNetworkConnectivityCheckStatus. +func (in *PodNetworkConnectivityCheckStatus) DeepCopy() *PodNetworkConnectivityCheckStatus { + if in == nil { + return nil + } + out := new(PodNetworkConnectivityCheckStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go new file mode 100644 index 000000000..8deef38ca --- /dev/null +++ b/vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go @@ -0,0 +1,93 @@ +package v1alpha1 + +// This file contains a collection of methods that can be used from go-restful to +// generate Swagger API documentation for its models. Please read this PR for more +// information on the implementation: https://github.com/emicklei/go-restful/pull/215 +// +// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if +// they are on one line! For multiple line or blocks that you want to ignore use ---. +// Any context after a --- is ignored. +// +// Those methods can be generated by using hack/update-swagger-docs.sh + +// AUTO-GENERATED FUNCTIONS START HERE +var map_LogEntry = map[string]string{ + "": "LogEntry records events", + "time": "Start time of check action.", + "success": "Success indicates if the log entry indicates a success or failure.", + "reason": "Reason for status in a machine readable format.", + "message": "Message explaining status in a human readable format.", + "latency": "Latency records how long the action mentioned in the entry took.", +} + +func (LogEntry) SwaggerDoc() map[string]string { + return map_LogEntry +} + +var map_OutageEntry = map[string]string{ + "": "OutageEntry records time period of an outage", + "start": "Start of outage detected", + "end": "End of outage detected", + "startLogs": "StartLogs contains log entries related to the start of this outage. Should contain the original failure, any entries where the failure mode changed.", + "endLogs": "EndLogs contains log entries related to the end of this outage. Should contain the success entry that resolved the outage and possibly a few of the failure log entries that preceded it.", + "message": "Message summarizes outage details in a human readable format.", +} + +func (OutageEntry) SwaggerDoc() map[string]string { + return map_OutageEntry +} + +var map_PodNetworkConnectivityCheck = map[string]string{ + "": "PodNetworkConnectivityCheck", + "spec": "Spec defines the source and target of the connectivity check", + "status": "Status contains the observed status of the connectivity check", +} + +func (PodNetworkConnectivityCheck) SwaggerDoc() map[string]string { + return map_PodNetworkConnectivityCheck +} + +var map_PodNetworkConnectivityCheckCondition = map[string]string{ + "": "PodNetworkConnectivityCheckCondition represents the overall status of the pod network connectivity.", + "type": "Type of the condition", + "status": "Status of the condition", + "reason": "Reason for the condition's last status transition in a machine readable format.", + "message": "Message indicating details about last transition in a human readable format.", + "lastTransitionTime": "Last time the condition transitioned from one status to another.", +} + +func (PodNetworkConnectivityCheckCondition) SwaggerDoc() map[string]string { + return map_PodNetworkConnectivityCheckCondition +} + +var map_PodNetworkConnectivityCheckList = map[string]string{ + "": "PodNetworkConnectivityCheckList is a collection of PodNetworkConnectivityCheck", + "items": "Items contains the items", +} + +func (PodNetworkConnectivityCheckList) SwaggerDoc() map[string]string { + return map_PodNetworkConnectivityCheckList +} + +var map_PodNetworkConnectivityCheckSpec = map[string]string{ + "sourcePod": "SourcePod names the pod from which the condition will be checked", + "targetEndpoint": "EndpointAddress to check. A TCP address of the form host:port. Note that if host is a DNS name, then the check would fail if the DNS name cannot be resolved. Specify an IP address for host to bypass DNS name lookup.", + "tlsClientCert": "TLSClientCert, if specified, references a kubernetes.io/tls type secret with 'tls.crt' and 'tls.key' entries containing an optional TLS client certificate and key to be used when checking endpoints that require a client certificate in order to gracefully preform the scan without causing excessive logging in the endpoint process. The secret must exist in the same namespace as this resource.", +} + +func (PodNetworkConnectivityCheckSpec) SwaggerDoc() map[string]string { + return map_PodNetworkConnectivityCheckSpec +} + +var map_PodNetworkConnectivityCheckStatus = map[string]string{ + "successes": "Successes contains logs successful check actions", + "failures": "Failures contains logs of unsuccessful check actions", + "outages": "Outages contains logs of time periods of outages", + "conditions": "Conditions summarize the status of the check", +} + +func (PodNetworkConnectivityCheckStatus) SwaggerDoc() map[string]string { + return map_PodNetworkConnectivityCheckStatus +} + +// AUTO-GENERATED FUNCTIONS END HERE diff --git a/vendor/modules.txt b/vendor/modules.txt index cbe92ac1a..322996cd8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -102,6 +102,7 @@ github.com/openshift/api/imageregistry/v1 github.com/openshift/api/network/v1 github.com/openshift/api/operator/v1 github.com/openshift/api/operator/v1alpha1 +github.com/openshift/api/operatorcontrolplane/v1alpha1 github.com/openshift/api/pkg/serialization github.com/openshift/api/security/v1 # github.com/openshift/client-go v0.0.0-20210409155308-a8e62c60e930 => github.com/openshift/client-go v0.0.0-20210409155308-a8e62c60e930 From 6b29ccc9c64ce2d5d9b47941c1d6bc35b67a0795 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Wed, 7 Apr 2021 21:54:34 +0200 Subject: [PATCH 06/20] Turn PNCC info into nested map --- .../pod_network_connectivity_checks.go | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 345bc5f8f..b5e5b85bf 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -65,15 +65,23 @@ func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient } } - msg := map[string]struct{}{} - reason := map[string]struct{}{} + reasons := map[string]map[string]int{} for _, entry := range unsuccessful { - msg[entry.Message] = struct{}{} - reason[entry.Reason] = struct{}{} + if _, reasonExist := reasons[entry.Reason]; reasonExist { + msgMap := reasons[entry.Reason] + if _, msgExist := msgMap[entry.Message]; msgExist { + msgMap[entry.Message]++ + } else { + msgMap[entry.Message] = 1 + } + } else { + reasons[entry.Reason] = map[string]int{ + entry.Message: 1, + } + } } return []record.Record{{Name: "config/podnetworkconnectivitychecks_unstruct", Item: record.JSONMarshaller{Object: pnccListUnstruct}}, {Name: "config/podnetworkconnectivitychecks_struct", Item: record.JSONMarshaller{Object: pnccListStruct}}, - {Name: "config/podnetworkconnectivitychecks_msg", Item: record.JSONMarshaller{Object: msg}}, - {Name: "config/podnetworkconnectivitychecks_reason", Item: record.JSONMarshaller{Object: reason}}}, nil + {Name: "config/podnetworkconnectivitychecks_reasons", Item: record.JSONMarshaller{Object: reasons}}}, nil } From 11bc36eb10c079751808a8d704e25f92b3495b8e Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Sun, 11 Apr 2021 20:38:39 +0200 Subject: [PATCH 07/20] Fix GatherPNCC function description --- pkg/gather/clusterconfig/pod_network_connectivity_checks.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index b5e5b85bf..c72fb650c 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -13,7 +13,7 @@ import ( "github.com/openshift/insights-operator/pkg/record" ) -// GatherPNCC collects PodNetworkConnectivityChecks. +// GatherPNCC collects a summary of failed PodNetworkConnectivityChecks. func GatherPNCC(g *Gatherer, c chan<- gatherResult) { gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) if err != nil { @@ -81,7 +81,5 @@ func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient } } - return []record.Record{{Name: "config/podnetworkconnectivitychecks_unstruct", Item: record.JSONMarshaller{Object: pnccListUnstruct}}, - {Name: "config/podnetworkconnectivitychecks_struct", Item: record.JSONMarshaller{Object: pnccListStruct}}, - {Name: "config/podnetworkconnectivitychecks_reasons", Item: record.JSONMarshaller{Object: reasons}}}, nil + return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: reasons}}}, nil } From 2b7b063021c3790fbbb540b2e5e535a3f1c3a125 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Sun, 11 Apr 2021 20:39:25 +0200 Subject: [PATCH 08/20] Add meaningful PNCC gathering test --- .../pod_network_connectivity_checks_test.go | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go new file mode 100644 index 000000000..7cd388123 --- /dev/null +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go @@ -0,0 +1,86 @@ +package clusterconfig + +import ( + "context" + "reflect" + "testing" + + "github.com/openshift/insights-operator/pkg/record" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + dynamicfake "k8s.io/client-go/dynamic/fake" + kubefake "k8s.io/client-go/kubernetes/fake" +) + +func Test_PNCC(t *testing.T) { + var pnccYAML = `apiVersion: controlplane.operator.openshift.io/v1alpha1 +kind: PodNetworkConnectivityCheck +metadata: + name: example-pncc + namespace: example-namespace +status: + failures: + - success: false + reason: TestReason + message: TestMessage +` + + pnccClient := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{ + pnccGroupVersionResource: "PodNetworkConnectivityChecksList", + }) + + decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + testPNCC := &unstructured.Unstructured{} + + _, _, err := decUnstructured.Decode([]byte(pnccYAML), nil, testPNCC) + if err != nil { + t.Fatal("unable to decode PNCC YAML", err) + } + + // Initialize the remaining K8s/OS fake clients. + coreClient := kubefake.NewSimpleClientset() + + records, errs := gatherPNCC(context.Background(), pnccClient, coreClient.CoreV1()) + if len(errs) > 0 { + t.Fatalf("unexpected errors in the first run: %#v", errs) + } + if len(records) != 1 { + t.Fatalf("unexpected number or records in the first run: %d", len(records)) + } + rec := records[0] + if rec.Name != "config/podnetworkconnectivitychecks" { + t.Fatalf("unexpected name of record in the first run: %q", rec.Name) + } + recItem, ok := rec.Item.(record.JSONMarshaller) + if !ok { + t.Fatalf("unexpected type of record item in the first run: %q", rec.Name) + } + if !reflect.DeepEqual(recItem.Object, map[string]map[string]int{}) { + t.Fatalf("unexpected value of record item in the first run: %#v", recItem) + } + + // Create the DataHubs resource and now the SCCs and CRBs should be gathered. + _, _ = pnccClient.Resource(pnccGroupVersionResource).Namespace("example-namespace").Create(context.Background(), testPNCC, metav1.CreateOptions{}) + + records, errs = gatherPNCC(context.Background(), pnccClient, coreClient.CoreV1()) + if len(errs) > 0 { + t.Fatalf("unexpected errors in the second run: %#v", errs) + } + if len(records) != 1 { + t.Fatalf("unexpected number or records in the second run: %d", len(records)) + } + rec = records[0] + if rec.Name != "config/podnetworkconnectivitychecks" { + t.Fatalf("unexpected name of record in the second run: %q", rec.Name) + } + recItem, ok = rec.Item.(record.JSONMarshaller) + if !ok { + t.Fatalf("unexpected type of record item in the second run: %q", rec.Name) + } + if !reflect.DeepEqual(recItem.Object, map[string]map[string]int{"TestReason": {"TestMessage": 1}}) { + t.Fatalf("unexpected value of record item in the second run: %#v", recItem) + } +} From 23624d34ad4258539dd578548cb1721188daa266 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Mon, 12 Apr 2021 01:51:02 +0200 Subject: [PATCH 09/20] Improve PNCC docs in code --- .../clusterconfig/pod_network_connectivity_checks.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index c72fb650c..5fbfcb6d6 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -14,6 +14,14 @@ import ( ) // GatherPNCC collects a summary of failed PodNetworkConnectivityChecks. +// The checks are requested via a dynamic client and +// then unmarshaled into the appropriate structure. +// +// Resource API: podnetworkconnectivitychecks.controlplane.operator.openshift.io/v1alpha1 +// Docs for relevant types: https://pkg.go.dev/github.com/openshift/api/operatorcontrolplane/v1alpha1 +// +// * Location in archive: config/podnetworkconnectivitychecks.json +// * Id in config: pod_network_connectivity_checks func GatherPNCC(g *Gatherer, c chan<- gatherResult) { gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) if err != nil { From 98919d6b9d5dcccac640643aa8c2e5952dd23886 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Mon, 12 Apr 2021 01:51:16 +0200 Subject: [PATCH 10/20] Update gathered-data.md with PNCC docs --- docs/gathered-data.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/gathered-data.md b/docs/gathered-data.md index 3bf981620..f1dc47990 100644 --- a/docs/gathered-data.md +++ b/docs/gathered-data.md @@ -421,6 +421,19 @@ Response see https://docs.openshift.com/container-platform/4.6/rest_api/workload * 4.7+ +## PNCC + +collects a summary of failed PodNetworkConnectivityChecks. +The checks are requested via a dynamic client and +then unmarshaled into the appropriate structure. + +Resource API: podnetworkconnectivitychecks.controlplane.operator.openshift.io/v1alpha1 +Docs for relevant types: https://pkg.go.dev/github.com/openshift/api/operatorcontrolplane/v1alpha1 + +* Location in archive: config/podnetworkconnectivitychecks.json +* Id in config: pod_network_connectivity_checks + + ## PodDisruptionBudgets gathers the cluster's PodDisruptionBudgets. From 2e4aa14d1972c184a3cb587912145bf9cea0154f Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Mon, 12 Apr 2021 01:55:45 +0200 Subject: [PATCH 11/20] Add PNCC JSON to sample archive --- .../config/podnetworkconnectivitychecks.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/insights-archive-sample/config/podnetworkconnectivitychecks.json diff --git a/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json b/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json new file mode 100644 index 000000000..591a916bf --- /dev/null +++ b/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json @@ -0,0 +1,8 @@ +{ + "TCPConnectError": { + "kubernetes-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.74.181.101:6443: dial tcp 10.74.181.101:6443: connect: connection refused": 6, + "kubernetes-apiserver-service-cluster: failed to establish a TCP connection to 172.30.101.107:443: dial tcp 172.30.101.107:443: connect: connection refused": 6, + "openshift-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.128.0.31:8443: dial tcp 10.128.0.31:8443: connect: connection refused": 3, + "openshift-apiserver-service-cluster: failed to establish a TCP connection to 172.30.137.129:443: dial tcp 172.30.137.129:443: i/o timeout": 3 + } +} From 3fee1e38430c08528f85220cd431febcf6abf3b9 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Tue, 13 Apr 2021 21:14:47 +0200 Subject: [PATCH 12/20] Fix PNCC reason/message counter logic Apparently Go has a nice idiomatic way of doing this task which is usually quite ugly in most other languages. --- .../pod_network_connectivity_checks.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 5fbfcb6d6..90618029a 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -75,18 +75,10 @@ func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient reasons := map[string]map[string]int{} for _, entry := range unsuccessful { - if _, reasonExist := reasons[entry.Reason]; reasonExist { - msgMap := reasons[entry.Reason] - if _, msgExist := msgMap[entry.Message]; msgExist { - msgMap[entry.Message]++ - } else { - msgMap[entry.Message] = 1 - } - } else { - reasons[entry.Reason] = map[string]int{ - entry.Message: 1, - } + if _, exists := reasons[entry.Reason]; !exists { + reasons[entry.Reason] = map[string]int{} } + reasons[entry.Reason][entry.Message]++ } return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: reasons}}}, nil From d7df396062b2ae261d6a17c23d2b5acdf66dbe0f Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Thu, 15 Apr 2021 13:43:51 +0200 Subject: [PATCH 13/20] Add version information to PNCC gatherer --- pkg/gather/clusterconfig/pod_network_connectivity_checks.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 90618029a..46fb91453 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -22,6 +22,8 @@ import ( // // * Location in archive: config/podnetworkconnectivitychecks.json // * Id in config: pod_network_connectivity_checks +// * Since versions: +// * 4.8+ func GatherPNCC(g *Gatherer, c chan<- gatherResult) { gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) if err != nil { From 6bf997f1e59cd1f069867ce7cd574fa3c2b331d0 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 16 Apr 2021 17:17:47 +0200 Subject: [PATCH 14/20] Gather time of last failed PNCC instead of count --- .../clusterconfig/pod_network_connectivity_checks.go | 9 ++++++--- .../pod_network_connectivity_checks_test.go | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index 46fb91453..dc6e50c59 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -3,6 +3,7 @@ package clusterconfig import ( "context" "encoding/json" + "time" controlplanev1 "github.com/openshift/api/operatorcontrolplane/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -75,12 +76,14 @@ func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient } } - reasons := map[string]map[string]int{} + reasons := map[string]map[string]time.Time{} for _, entry := range unsuccessful { if _, exists := reasons[entry.Reason]; !exists { - reasons[entry.Reason] = map[string]int{} + reasons[entry.Reason] = map[string]time.Time{} + } + if oldTime, exists := reasons[entry.Reason][entry.Message]; !exists || entry.Start.After(oldTime) { + reasons[entry.Reason][entry.Message] = entry.Start.Time } - reasons[entry.Reason][entry.Message]++ } return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: reasons}}}, nil diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go index 7cd388123..e4872ade0 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go @@ -4,6 +4,7 @@ import ( "context" "reflect" "testing" + "time" "github.com/openshift/insights-operator/pkg/record" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -58,7 +59,7 @@ status: if !ok { t.Fatalf("unexpected type of record item in the first run: %q", rec.Name) } - if !reflect.DeepEqual(recItem.Object, map[string]map[string]int{}) { + if !reflect.DeepEqual(recItem.Object, map[string]map[string]time.Time{}) { t.Fatalf("unexpected value of record item in the first run: %#v", recItem) } @@ -80,7 +81,7 @@ status: if !ok { t.Fatalf("unexpected type of record item in the second run: %q", rec.Name) } - if !reflect.DeepEqual(recItem.Object, map[string]map[string]int{"TestReason": {"TestMessage": 1}}) { + if !reflect.DeepEqual(recItem.Object, map[string]map[string]time.Time{"TestReason": {"TestMessage": time.Time{}}}) { t.Fatalf("unexpected value of record item in the second run: %#v", recItem) } } From 643628ff23fcb621ec10fa864676392607acffda Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Thu, 22 Apr 2021 12:28:44 +0200 Subject: [PATCH 15/20] Fix PNCC test comments --- .../pod_network_connectivity_checks_test.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go index e4872ade0..efc9e8083 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go @@ -13,7 +13,6 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer/yaml" dynamicfake "k8s.io/client-go/dynamic/fake" - kubefake "k8s.io/client-go/kubernetes/fake" ) func Test_PNCC(t *testing.T) { @@ -41,10 +40,8 @@ status: t.Fatal("unable to decode PNCC YAML", err) } - // Initialize the remaining K8s/OS fake clients. - coreClient := kubefake.NewSimpleClientset() - - records, errs := gatherPNCC(context.Background(), pnccClient, coreClient.CoreV1()) + // Check before creating the PNCC. + records, errs := gatherPNCC(context.Background(), pnccClient) if len(errs) > 0 { t.Fatalf("unexpected errors in the first run: %#v", errs) } @@ -63,10 +60,11 @@ status: t.Fatalf("unexpected value of record item in the first run: %#v", recItem) } - // Create the DataHubs resource and now the SCCs and CRBs should be gathered. + // Create the PNCC resource. _, _ = pnccClient.Resource(pnccGroupVersionResource).Namespace("example-namespace").Create(context.Background(), testPNCC, metav1.CreateOptions{}) - records, errs = gatherPNCC(context.Background(), pnccClient, coreClient.CoreV1()) + // Check after creating the PNCC. + records, errs = gatherPNCC(context.Background(), pnccClient) if len(errs) > 0 { t.Fatalf("unexpected errors in the second run: %#v", errs) } From 5cafdc4ac054b88de523ba2c98bc9af9210804d6 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Thu, 22 Apr 2021 12:28:58 +0200 Subject: [PATCH 16/20] Remove coreClient from PNCC gatherer --- .../clusterconfig/pod_network_connectivity_checks.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index dc6e50c59..e0b67a2cb 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -8,8 +8,6 @@ import ( controlplanev1 "github.com/openshift/api/operatorcontrolplane/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" - "k8s.io/client-go/kubernetes" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "github.com/openshift/insights-operator/pkg/record" ) @@ -31,13 +29,8 @@ func GatherPNCC(g *Gatherer, c chan<- gatherResult) { c <- gatherResult{errors: []error{err}} return } - gatherKubeClient, err := kubernetes.NewForConfig(g.gatherProtoKubeConfig) - if err != nil { - c <- gatherResult{errors: []error{err}} - return - } - records, errors := gatherPNCC(g.ctx, gatherDynamicClient, gatherKubeClient.CoreV1()) + records, errors := gatherPNCC(g.ctx, gatherDynamicClient) c <- gatherResult{records: records, errors: errors} } @@ -51,7 +44,7 @@ func getUnsuccessfulChecks(entries []controlplanev1.LogEntry) []controlplanev1.L return unsuccesseful } -func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface, coreClient corev1client.CoreV1Interface) ([]record.Record, []error) { +func gatherPNCC(ctx context.Context, dynamicClient dynamic.Interface) ([]record.Record, []error) { pnccListUnstruct, err := dynamicClient.Resource(pnccGroupVersionResource).List(ctx, metav1.ListOptions{}) if err != nil { return nil, []error{err} From 884b0d2f88e6de3a6df63340224afcb5e2b0a7fe Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 23 Apr 2021 12:34:47 +0200 Subject: [PATCH 17/20] Add detailed note to PNCC gatherer comment --- pkg/gather/clusterconfig/pod_network_connectivity_checks.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go index e0b67a2cb..d376d04cc 100644 --- a/pkg/gather/clusterconfig/pod_network_connectivity_checks.go +++ b/pkg/gather/clusterconfig/pod_network_connectivity_checks.go @@ -13,6 +13,7 @@ import ( ) // GatherPNCC collects a summary of failed PodNetworkConnectivityChecks. +// Time of the most recently failed check with each reason and message is recorded. // The checks are requested via a dynamic client and // then unmarshaled into the appropriate structure. // From 3ff97a8ef66721b84d873973ee4961eeb7ffa0da Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 23 Apr 2021 12:35:08 +0200 Subject: [PATCH 18/20] Update PNCC docs in gathered-data --- docs/gathered-data.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/gathered-data.md b/docs/gathered-data.md index f1dc47990..bb1aae1ea 100644 --- a/docs/gathered-data.md +++ b/docs/gathered-data.md @@ -424,6 +424,7 @@ Response see https://docs.openshift.com/container-platform/4.6/rest_api/workload ## PNCC collects a summary of failed PodNetworkConnectivityChecks. +Time of the most recently failed check with each reason and message is recorded. The checks are requested via a dynamic client and then unmarshaled into the appropriate structure. @@ -432,6 +433,8 @@ Docs for relevant types: https://pkg.go.dev/github.com/openshift/api/operatorcon * Location in archive: config/podnetworkconnectivitychecks.json * Id in config: pod_network_connectivity_checks +* Since versions: + * 4.8+ ## PodDisruptionBudgets From e7c31639dff16de1fb018741a548f3324304d46b Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 23 Apr 2021 12:35:25 +0200 Subject: [PATCH 19/20] Update PNCC in sample archive --- .../config/podnetworkconnectivitychecks.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json b/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json index 591a916bf..8bf146549 100644 --- a/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json +++ b/docs/insights-archive-sample/config/podnetworkconnectivitychecks.json @@ -1,8 +1,8 @@ { "TCPConnectError": { - "kubernetes-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.74.181.101:6443: dial tcp 10.74.181.101:6443: connect: connection refused": 6, - "kubernetes-apiserver-service-cluster: failed to establish a TCP connection to 172.30.101.107:443: dial tcp 172.30.101.107:443: connect: connection refused": 6, - "openshift-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.128.0.31:8443: dial tcp 10.128.0.31:8443: connect: connection refused": 3, - "openshift-apiserver-service-cluster: failed to establish a TCP connection to 172.30.137.129:443: dial tcp 172.30.137.129:443: i/o timeout": 3 + "kubernetes-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.0.89.232:6443: dial tcp 10.0.89.232:6443: connect: connection refused": "2021-04-22T13:33:02+02:00", + "kubernetes-apiserver-service-cluster: failed to establish a TCP connection to 172.30.254.69:443: dial tcp 172.30.254.69:443: connect: connection refused": "2021-04-22T13:33:02+02:00", + "openshift-apiserver-endpoint-master-0: failed to establish a TCP connection to 10.128.0.43:8443: dial tcp 10.128.0.43:8443: connect: connection refused": "2021-04-21T14:09:02+02:00", + "openshift-apiserver-service-cluster: failed to establish a TCP connection to 172.30.140.22:443: dial tcp 172.30.140.22:443: i/o timeout": "2021-04-21T14:09:02+02:00" } } From 5c84f1f439b67e27944f9db1c07e6459cc9efae9 Mon Sep 17 00:00:00 2001 From: Ivo Meixner Date: Fri, 23 Apr 2021 13:35:24 +0200 Subject: [PATCH 20/20] Make PNCC gatherer failable --- pkg/gather/clusterconfig/0_gatherer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/gather/clusterconfig/0_gatherer.go b/pkg/gather/clusterconfig/0_gatherer.go index bff5e51cc..26ff98bd9 100644 --- a/pkg/gather/clusterconfig/0_gatherer.go +++ b/pkg/gather/clusterconfig/0_gatherer.go @@ -104,7 +104,7 @@ var gatherFunctions = map[string]gathering{ "sap_pods": failable(GatherSAPPods), "sap_datahubs": failable(GatherSAPDatahubs), "olm_operators": failable(GatherOLMOperators), - "pod_network_connectivity_checks": important(GatherPNCC), + "pod_network_connectivity_checks": failable(GatherPNCC), } // New creates new Gatherer