Skip to content

Commit 0371ec5

Browse files
author
Ricardo Lüders
authored
OCPBUGS-23962: adds helm information gather (#868)
* feat: gather helm information * feat(gather/hemlchart_info): add resource counter * docs: adds helmchart_info sample data * test: adds some unit test for helmchart_gather * test: expanding gather_helm_info tests * feat: adding helmchart gatherer * docs: update gathered-data * Revert "feat: adding helmchart gatherer" This reverts commit e57a5c1. * refactor: revert helmcharts gatherer * style: fix lint errors * refactor(helm_info): remove if for label checking * refactor(gather_info): unexpose labelChartNameKey const * fix: wrong log message * test(helm_gather_info): invalid resources
1 parent 9b91ed4 commit 0371ec5

10 files changed

+678
-4
lines changed

docs/gathered-data.md

+24
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,30 @@ None
766766
None
767767

768768

769+
## HelmInfo
770+
771+
Collects summarized info about the helm usage on a cluster
772+
in a generic fashion
773+
774+
### API Reference
775+
None
776+
777+
### Sample data
778+
- [docs/insights-archive-sample/config/helmchart_info.json](./insights-archive-sample/config/helmchart_info.json)
779+
780+
### Location in archive
781+
- `config/helmchart_info.json`
782+
783+
### Config ID
784+
`workloads/helmchart_info`
785+
786+
### Released version
787+
- 4.15.0
788+
789+
### Backported versions
790+
None
791+
792+
769793
## HostSubnet
770794

771795
Collects `HostSubnet` information.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"6c7e03817b27fbe6cc67ae835381df521b8d847dd029fb2df483f1a327b63582": [
3+
{ "name": "jenkins", "version": "0.0.3", "resources": { "services": 2 } }
4+
],
5+
"a085ddf97d8c556fd4f965392f7d3446c4b11df0544848878e06b20c96523064": [
6+
{
7+
"name": "nodejs",
8+
"version": "",
9+
"resources": { "deployments": 1, "replicasets": 1, "services": 1 }
10+
}
11+
],
12+
"e976fcc461dc1b1ad177c083135393e90ded18b3707987d4b6adf78e86e687ed": [
13+
{
14+
"name": "nodejs",
15+
"version": "",
16+
"resources": { "deployments": 1, "replicasets": 1, "services": 1 }
17+
}
18+
]
19+
}

pkg/gather/gather.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func CreateAllGatherers(
6868
gatherKubeConfig, gatherProtoKubeConfig, metricsGatherKubeConfig, alertsGatherKubeConfig,
6969
anonymizer, configObserver,
7070
)
71-
workloadsGatherer := workloads.New(gatherProtoKubeConfig)
71+
workloadsGatherer := workloads.New(gatherKubeConfig, gatherProtoKubeConfig)
7272
conditionalGatherer := conditional.New(
7373
gatherProtoKubeConfig, metricsGatherKubeConfig, gatherKubeConfig, configObserver, insightsClient,
7474
)
+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package workloads
2+
3+
import (
4+
"context"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
"fmt"
8+
"strings"
9+
10+
"k8s.io/apimachinery/pkg/runtime/schema"
11+
12+
"k8s.io/apimachinery/pkg/api/errors"
13+
"k8s.io/client-go/dynamic"
14+
"k8s.io/klog/v2"
15+
16+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17+
18+
"github.com/openshift/insights-operator/pkg/record"
19+
)
20+
21+
const labelChartNameKey = "helm.sh/chart"
22+
23+
// GatherHelmInfo Collects summarized info about the helm usage on a cluster
24+
// in a generic fashion
25+
//
26+
// ### API Reference
27+
// None
28+
//
29+
// ### Sample data
30+
// - docs/insights-archive-sample/config/helmchart_info.json
31+
//
32+
// ### Location in archive
33+
// - `config/helmchart_info.json`
34+
//
35+
// ### Config ID
36+
// `workloads/helmchart_info`
37+
//
38+
// ### Released version
39+
// - 4.15.0
40+
//
41+
// ### Backported versions
42+
// None
43+
func (g *Gatherer) GatherHelmInfo(ctx context.Context) ([]record.Record, []error) {
44+
dynamicClient, err := dynamic.NewForConfig(g.gatherKubeConfig)
45+
if err != nil {
46+
return nil, []error{err}
47+
}
48+
49+
return gatherHelmInfo(ctx, dynamicClient)
50+
}
51+
52+
func gatherHelmInfo(
53+
ctx context.Context,
54+
dynamicClient dynamic.Interface,
55+
) ([]record.Record, []error) {
56+
resources := []schema.GroupVersionResource{
57+
{Group: "apps", Version: "v1", Resource: "replicasets"},
58+
{Group: "apps", Version: "v1", Resource: "daemonsets"},
59+
{Group: "apps", Version: "v1", Resource: "statefulsets"},
60+
{Group: "", Version: "v1", Resource: "services"},
61+
{Group: "apps", Version: "v1", Resource: "deployments"},
62+
}
63+
64+
var errs []error
65+
var records []record.Record
66+
helmList := newHelmChartInfoList()
67+
68+
for _, resource := range resources {
69+
listOptions := metav1.ListOptions{LabelSelector: "app.kubernetes.io/managed-by=Helm"}
70+
71+
items, err := dynamicClient.Resource(resource).List(ctx, listOptions)
72+
if errors.IsNotFound(err) {
73+
return nil, nil
74+
}
75+
if err != nil {
76+
klog.V(2).Infof("Unable to list %s resource due to: %s", resource, err)
77+
errs = append(errs, err)
78+
continue
79+
}
80+
81+
for _, item := range items.Items {
82+
labels := item.GetLabels()
83+
84+
// Anonymize the namespace to make it unique identifier
85+
hash, err := createHash(item.GetNamespace())
86+
if err != nil {
87+
klog.Errorf("unable to hash the namespace '%s': %v", labels[labelChartNameKey], err)
88+
continue
89+
}
90+
91+
name, version := helmChartNameAndVersion(labels[labelChartNameKey])
92+
if name == "" && version == "" {
93+
// some helm-maneged resource may not have reference to the chart
94+
klog.Infof("unable to get HelmChart from %s on %s from %s.", resource.Resource, item.GetNamespace(), item.GetName())
95+
continue
96+
}
97+
98+
helmList.addItem(hash, resource.Resource, HelmChartInfo{
99+
Name: name,
100+
Version: version,
101+
})
102+
}
103+
}
104+
105+
if len(helmList.Namespaces) > 0 {
106+
records = []record.Record{
107+
{
108+
Name: "config/helmchart_info",
109+
Item: record.JSONMarshaller{Object: &helmList.Namespaces},
110+
},
111+
}
112+
}
113+
114+
if len(errs) > 0 {
115+
return records, errs
116+
}
117+
118+
return records, nil
119+
}
120+
121+
func createHash(chartName string) (string, error) {
122+
h := sha256.New()
123+
_, err := h.Write([]byte(chartName))
124+
if err != nil {
125+
return "", err
126+
}
127+
128+
hashInBytes := h.Sum(nil)
129+
hash := hex.EncodeToString(hashInBytes)
130+
131+
return hash, nil
132+
}
133+
134+
func helmChartNameAndVersion(chart string) (name, version string) {
135+
parts := strings.Split(chart, "-")
136+
137+
// no version found
138+
if len(parts) == 1 {
139+
return chart, ""
140+
}
141+
142+
name = strings.Join(parts[:len(parts)-1], "-")
143+
144+
// best guess to get the version
145+
version = parts[len(parts)-1]
146+
// check for standard version format
147+
if !strings.Contains(version, ".") {
148+
// maybe it is a string version
149+
if !isStringVersion(version) {
150+
// not a valid version, add to name and version should be empty
151+
name = fmt.Sprintf("%s-%s", name, version)
152+
version = ""
153+
}
154+
}
155+
156+
return name, version
157+
}
158+
159+
func isStringVersion(version string) bool {
160+
stringVersions := []string{"latest", "beta", "alpha"}
161+
for _, v := range stringVersions {
162+
if v == version {
163+
return true
164+
}
165+
}
166+
return false
167+
}

0 commit comments

Comments
 (0)