-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1949907: Gather summary of PodNetworkConnectivityChecks (#374)
* Add permissions for PodNetworkConnectivityChecks * Gather PodNetworkConnectivityChecks * Fix PNCC name * Gather PNCC info * Vendor PNCC struct * Turn PNCC info into nested map * Fix GatherPNCC function description * Add meaningful PNCC gathering test * Improve PNCC docs in code * Update gathered-data.md with PNCC docs * Add PNCC JSON to sample archive * 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. * Add version information to PNCC gatherer * Gather time of last failed PNCC instead of count * Fix PNCC test comments * Remove coreClient from PNCC gatherer * Add detailed note to PNCC gatherer comment * Update PNCC docs in gathered-data * Update PNCC in sample archive * Make PNCC gatherer failable
- Loading branch information
Showing
14 changed files
with
984 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
docs/insights-archive-sample/config/podnetworkconnectivitychecks.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"TCPConnectError": { | ||
"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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
pkg/gather/clusterconfig/pod_network_connectivity_checks.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package clusterconfig | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"time" | ||
|
||
controlplanev1 "github.com/openshift/api/operatorcontrolplane/v1alpha1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/dynamic" | ||
|
||
"github.com/openshift/insights-operator/pkg/record" | ||
) | ||
|
||
// 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. | ||
// | ||
// 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 | ||
// * Since versions: | ||
// * 4.8+ | ||
func GatherPNCC(g *Gatherer, c chan<- gatherResult) { | ||
gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) | ||
if err != nil { | ||
c <- gatherResult{errors: []error{err}} | ||
return | ||
} | ||
|
||
records, errors := gatherPNCC(g.ctx, gatherDynamicClient) | ||
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) ([]record.Record, []error) { | ||
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} | ||
} | ||
|
||
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)...) | ||
} | ||
} | ||
|
||
reasons := map[string]map[string]time.Time{} | ||
for _, entry := range unsuccessful { | ||
if _, exists := reasons[entry.Reason]; !exists { | ||
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 | ||
} | ||
} | ||
|
||
return []record.Record{{Name: "config/podnetworkconnectivitychecks", Item: record.JSONMarshaller{Object: reasons}}}, nil | ||
} |
85 changes: 85 additions & 0 deletions
85
pkg/gather/clusterconfig/pod_network_connectivity_checks_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package clusterconfig | ||
|
||
import ( | ||
"context" | ||
"reflect" | ||
"testing" | ||
"time" | ||
|
||
"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" | ||
) | ||
|
||
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) | ||
} | ||
|
||
// 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) | ||
} | ||
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]time.Time{}) { | ||
t.Fatalf("unexpected value of record item in the first run: %#v", recItem) | ||
} | ||
|
||
// Create the PNCC resource. | ||
_, _ = pnccClient.Resource(pnccGroupVersionResource).Namespace("example-namespace").Create(context.Background(), testPNCC, metav1.CreateOptions{}) | ||
|
||
// 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) | ||
} | ||
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]time.Time{"TestReason": {"TestMessage": time.Time{}}}) { | ||
t.Fatalf("unexpected value of record item in the second run: %#v", recItem) | ||
} | ||
} |
Oops, something went wrong.