diff --git a/docs/gathered-data.md b/docs/gathered-data.md index f7ac1c197..1bd034bf7 100644 --- a/docs/gathered-data.md +++ b/docs/gathered-data.md @@ -50,6 +50,20 @@ The CRD sizes above are in the raw (uncompressed) state. * Id in config: clusterconfig/crds +## CephCluster + +collects statuses of the`cephclusters.ceph.rook.io` resources +from Openshift Data Foundation Stack. + +API Reference: + https://github.com/rook/rook/blob/master/pkg/apis/ceph.rook.io/v1/types.go + +* Location in archive: config/storage//.json +* Id in config: clusterconfig/ceph_cluster +* Since versions: + * 4.12+ + + ## CertificateSigningRequests collects anonymized CertificateSigningRequests. diff --git a/docs/insights-archive-sample/config/storage/openshift-storage/ocs-storagecluster-cephcluster.json b/docs/insights-archive-sample/config/storage/openshift-storage/ocs-storagecluster-cephcluster.json new file mode 100644 index 000000000..8ad240633 --- /dev/null +++ b/docs/insights-archive-sample/config/storage/openshift-storage/ocs-storagecluster-cephcluster.json @@ -0,0 +1,48 @@ +{ + "ceph": { + "capacity": {}, + "details": { + "MDS_ALL_DOWN": { + "message": "1 filesystem is offline", + "severity": "HEALTH_ERR" + }, + "MDS_UP_LESS_THAN_MAX": { + "message": "1 filesystem is online with fewer MDS than max_mds", + "severity": "HEALTH_WARN" + }, + "MGR_DOWN": { + "message": "no active mgr", + "severity": "HEALTH_WARN" + } + }, + "health": "HEALTH_ERR", + "lastChanged": "2022-07-18T13:43:24Z", + "lastChecked": "2022-07-19T07:29:56Z", + "previousHealth": "HEALTH_WARN", + "versions": { + "mon": { + "ceph version 16.2.7-112.el8cp (e18db2ff03ac60c64a18f3315c032b9d5a0a3b8f) pacific (stable)": 3 + }, + "overall": { + "ceph version 16.2.7-112.el8cp (e18db2ff03ac60c64a18f3315c032b9d5a0a3b8f) pacific (stable)": 3 + } + } + }, + "conditions": [ + { + "lastHeartbeatTime": "2022-07-19T07:29:56Z", + "lastTransitionTime": "2022-07-18T13:42:23Z", + "message": "Cluster created successfully", + "reason": "ClusterCreated", + "status": "True", + "type": "Ready" + } + ], + "message": "Cluster created successfully", + "phase": "Ready", + "state": "Created", + "version": { + "image": "registry.redhat.io/rhceph/rhceph-5-rhel8@sha256:b20a05e20403da9775408fde0f10ba9c81c5610a3814db7786dd43ce4b2aba4a", + "version": "16.2.7-112" + } +} \ No newline at end of file diff --git a/manifests/03-clusterrole.yaml b/manifests/03-clusterrole.yaml index 4a68ef84d..29622a7df 100644 --- a/manifests/03-clusterrole.yaml +++ b/manifests/03-clusterrole.yaml @@ -263,6 +263,13 @@ rules: verbs: - get - list + - apiGroups: + - ceph.rook.io + resources: + - cephclusters + verbs: + - get + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/pkg/gatherers/clusterconfig/ceph_cluster.go b/pkg/gatherers/clusterconfig/ceph_cluster.go new file mode 100644 index 000000000..f8de7df5b --- /dev/null +++ b/pkg/gatherers/clusterconfig/ceph_cluster.go @@ -0,0 +1,54 @@ +//nolint: dupl +package clusterconfig + +import ( + "context" + "fmt" + + "github.com/openshift/insights-operator/pkg/record" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/dynamic" + "k8s.io/klog/v2" +) + +// GatherCephCluster collects statuses of the`cephclusters.ceph.rook.io` resources +// from Openshift Data Foundation Stack. +// +// API Reference: +// https://github.com/rook/rook/blob/master/pkg/apis/ceph.rook.io/v1/types.go +// +// * Location in archive: config/storage//.json +// * Id in config: clusterconfig/ceph_cluster +// * Since versions: +// * 4.12+ +func (g *Gatherer) GatherCephCluster(ctx context.Context) ([]record.Record, []error) { + gatherDynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig) + if err != nil { + return nil, []error{err} + } + + return gatherCephCluster(ctx, gatherDynamicClient) +} + +func gatherCephCluster(ctx context.Context, dynamicClient dynamic.Interface) ([]record.Record, []error) { + cephClusterList, err := dynamicClient.Resource(cephClustereResource).List(ctx, metav1.ListOptions{}) + if errors.IsNotFound(err) { + return nil, nil + } + if err != nil { + klog.V(2).Infof("Unable to list %s resource due to: %s", gatherCephCluster, err) + return nil, []error{err} + } + + var records []record.Record + for i := range cephClusterList.Items { + item := &cephClusterList.Items[i] + status := item.Object["status"] + records = append(records, record.Record{ + Name: fmt.Sprintf("config/storage/%s/%s", item.GetNamespace(), item.GetName()), + Item: record.JSONMarshaller{Object: status}, + }) + } + return records, nil +} diff --git a/pkg/gatherers/clusterconfig/ceph_cluster_test.go b/pkg/gatherers/clusterconfig/ceph_cluster_test.go new file mode 100644 index 000000000..3829a4751 --- /dev/null +++ b/pkg/gatherers/clusterconfig/ceph_cluster_test.go @@ -0,0 +1,68 @@ +package clusterconfig + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + 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_GatherCephCluster(t *testing.T) { + var cephClusterYAML = ` +apiVersion: ceph.rook.io/v1 +kind: CephCluster +metadata: + name: ocs-storagecluster-cephcluster + namespace: openshift-storage +status: + attribute1: value1 + ceph: + phase: Ready + health: HEALTH_ERROR +` + + dynamicClient := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), map[schema.GroupVersionResource]string{ + cephClustereResource: "CephClustersList", + }) + decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + testCephCluster := &unstructured.Unstructured{} + + _, _, err := decUnstructured.Decode([]byte(cephClusterYAML), nil, testCephCluster) + if err != nil { + t.Fatal("unable to decode cephcluster ", err) + } + _, err = dynamicClient.Resource(cephClustereResource). + Namespace("openshift-storage"). + Create(context.Background(), testCephCluster, metav1.CreateOptions{}) + if err != nil { + t.Fatal("unable to create fake cephcluster ", err) + } + + records, errs := gatherCephCluster(context.Background(), dynamicClient) + assert.Len(t, errs, 0, "unexpected errors when gathering CephCluster resource") + assert.Len(t, records, 1) + + recordData, err := records[0].Item.Marshal() + assert.NoError(t, err) + var recordedCephCluster map[string]interface{} + err = json.Unmarshal(recordData, &recordedCephCluster) + assert.NoError(t, err) + + a1, ok, err := unstructured.NestedString(recordedCephCluster, "attribute1") + assert.NoError(t, err) + assert.True(t, ok) + assert.Equal(t, "value1", a1) + + ceph, ok, err := unstructured.NestedMap(recordedCephCluster, "ceph") + assert.NoError(t, err) + assert.True(t, ok) + assert.Equal(t, "Ready", ceph["phase"]) + assert.Equal(t, "HEALTH_ERROR", ceph["health"]) +} diff --git a/pkg/gatherers/clusterconfig/clusterconfig_gatherer.go b/pkg/gatherers/clusterconfig/clusterconfig_gatherer.go index 8d3905f5e..c2755ea26 100644 --- a/pkg/gatherers/clusterconfig/clusterconfig_gatherer.go +++ b/pkg/gatherers/clusterconfig/clusterconfig_gatherer.go @@ -83,6 +83,7 @@ var gatheringFunctions = map[string]gathererFuncPtr{ "overlapping_namespace_uids": (*Gatherer).GatherNamespacesWithOverlappingUIDs, "support_secret": (*Gatherer).GatherSupportSecret, "active_alerts": (*Gatherer).GatherActiveAlerts, + "ceph_cluster": (*Gatherer).GatherCephCluster, } func New( diff --git a/pkg/gatherers/clusterconfig/const.go b/pkg/gatherers/clusterconfig/const.go index 036d071ab..fecb63edb 100644 --- a/pkg/gatherers/clusterconfig/const.go +++ b/pkg/gatherers/clusterconfig/const.go @@ -52,11 +52,12 @@ var ( openshiftStorageResource = schema.GroupVersionResource{ Group: "ocs.openshift.io", Version: "v1", Resource: "storageclusters", } - + cephClustereResource = schema.GroupVersionResource{ + Group: "ceph.rook.io", Version: "v1", Resource: "cephclusters", + } jaegerResource = schema.GroupVersionResource{ Group: "jaegertracing.io", Version: "v1", Resource: "jaegers", } - costManagementMetricsConfigResource = schema.GroupVersionResource{ Group: "costmanagement-metrics-cfg.openshift.io", Version: "v1beta1", Resource: "costmanagementmetricsconfigs", }