Skip to content

Commit 32700a5

Browse files
committed
fetch helm release resources via helm sdk
1 parent 6d7b853 commit 32700a5

File tree

9 files changed

+118
-20
lines changed

9 files changed

+118
-20
lines changed

cyclops-ctrl/cmd/main/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func main() {
103103

104104
prometheus.StartCacheMetricsUpdater(&monitor, templatesRepo.ReturnCache(), 10*time.Second, setupLog)
105105

106-
helmReleaseClient := helm.NewReleaseClient(helmWatchNamespace)
106+
helmReleaseClient := helm.NewReleaseClient(helmWatchNamespace, k8sClient)
107107
gitWriteClient := git.NewWriteClient(credsResolver, getCommitMessageTemplate(), setupLog)
108108

109109
handler, err := handler.New(templatesRepo, k8sClient, helmReleaseClient, renderer, gitWriteClient, moduleTargetNamespace, telemetryClient, monitor)

cyclops-ctrl/internal/controller/helm.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ func (h *Helm) UninstallRelease(ctx *gin.Context) {
117117
func (h *Helm) GetReleaseResources(ctx *gin.Context) {
118118
ctx.Header("Access-Control-Allow-Origin", "*")
119119

120+
namespace := ctx.Param("namespace")
120121
name := ctx.Param("name")
121122

122-
resources, err := h.kubernetesClient.GetResourcesForRelease(name)
123+
resources, err := h.releaseClient.ListResources(namespace, name)
123124
if err != nil {
124125
fmt.Println(err)
125126
ctx.JSON(http.StatusBadRequest, dto.NewError("Error fetching Helm release resources", err.Error()))

cyclops-ctrl/internal/controller/sse/resources.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (s *Server) Resources(ctx *gin.Context) {
2525
}
2626

2727
func (s *Server) ReleaseResources(ctx *gin.Context) {
28-
resources, err := s.k8sClient.GetWorkloadsForRelease(ctx.Param("name"))
28+
resources, err := s.releaseClient.ListWorkloadsForRelease(ctx.Param("namespace"), ctx.Param("name"))
2929
if err != nil {
3030
ctx.String(http.StatusInternalServerError, err.Error())
3131
return

cyclops-ctrl/internal/controller/sse/server.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package sse
22

33
import (
4+
"github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/integrations/helm"
45
"github.com/gin-gonic/gin"
56

67
"github.com/cyclops-ui/cyclops/cyclops-ctrl/pkg/cluster/k8sclient"
78
)
89

910
type Server struct {
10-
k8sClient k8sclient.IKubernetesClient
11+
k8sClient k8sclient.IKubernetesClient
12+
releaseClient *helm.ReleaseClient
1113
}
1214

1315
// Initialize event and Start procnteessing requests
14-
func NewServer(k8sClient k8sclient.IKubernetesClient) *Server {
16+
func NewServer(k8sClient k8sclient.IKubernetesClient, releaseClient *helm.ReleaseClient) *Server {
1517
server := &Server{
16-
k8sClient: k8sClient,
18+
k8sClient: k8sClient,
19+
releaseClient: releaseClient,
1720
}
1821

1922
return server

cyclops-ctrl/internal/handler/handler.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ func (h *Handler) Start() error {
6262

6363
h.router = gin.New()
6464

65-
server := sse.NewServer(h.k8sClient)
65+
server := sse.NewServer(h.k8sClient, h.releaseClient)
6666

6767
h.router.GET("/stream/resources/:name", sse.HeadersMiddleware(), server.Resources)
68-
h.router.GET("/stream/releases/resources/:name", sse.HeadersMiddleware(), server.ReleaseResources)
68+
h.router.GET("/stream/releases/:namespace/:name/resources", sse.HeadersMiddleware(), server.ReleaseResources)
6969
h.router.POST("/stream/resources", sse.HeadersMiddleware(), server.SingleResource)
7070

7171
h.router.GET("/ping", h.pong())

cyclops-ctrl/internal/integrations/helm/helm.go

+85-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ package helm
22

33
import (
44
"fmt"
5+
"github.com/cyclops-ui/cyclops/cyclops-ctrl/internal/models/dto"
6+
"github.com/cyclops-ui/cyclops/cyclops-ctrl/pkg/cluster/k8sclient"
57
"io"
8+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9+
"k8s.io/apimachinery/pkg/runtime"
610
"log"
11+
"sort"
712
"strings"
813

914
"github.com/pkg/errors"
@@ -16,14 +21,18 @@ import (
1621

1722
type ReleaseClient struct {
1823
namespace string
24+
k8sClient k8sclient.IKubernetesClient
1925
}
2026

21-
func NewReleaseClient(namespace string) *ReleaseClient {
27+
func NewReleaseClient(namespace string, k8sClient k8sclient.IKubernetesClient) *ReleaseClient {
2228
return &ReleaseClient{
2329
namespace: strings.TrimSpace(namespace),
30+
k8sClient: k8sClient,
2431
}
2532
}
2633

34+
func noopLogger(format string, v ...interface{}) {}
35+
2736
func (r *ReleaseClient) ListReleases() ([]*release.Release, error) {
2837
settings := cli.New()
2938

@@ -119,3 +128,78 @@ func (r *ReleaseClient) UpgradeRelease(
119128
_, err = client.Run(name, current.Chart, values)
120129
return err
121130
}
131+
132+
func (r *ReleaseClient) ListResources(namespace string, name string) ([]dto.Resource, error) {
133+
if len(r.namespace) > 0 && namespace != r.namespace {
134+
return nil, errors.New(fmt.Sprintf("invalid namespace provided: %v", namespace))
135+
}
136+
137+
settings := cli.New()
138+
settings.SetNamespace(namespace)
139+
140+
actionConfig := new(action.Configuration)
141+
if err := actionConfig.Init(settings.RESTClientGetter(), namespace, "", noopLogger); err != nil {
142+
return nil, err
143+
}
144+
145+
client := action.NewStatus(actionConfig)
146+
client.ShowResources = true
147+
148+
releaseStatus, err := client.Run(name)
149+
if err != nil {
150+
return nil, err
151+
}
152+
153+
if releaseStatus.Info == nil {
154+
return nil, errors.New("empty release info resources")
155+
}
156+
157+
out := make([]dto.Resource, 0, 0)
158+
for gv, objs := range releaseStatus.Info.Resources {
159+
if strings.HasSuffix(gv, "(related)") {
160+
continue
161+
}
162+
163+
for _, obj := range objs {
164+
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
165+
if err != nil {
166+
return nil, err
167+
}
168+
169+
res, err := r.k8sClient.MapUnstructuredResource(unstructured.Unstructured{Object: u})
170+
if err != nil {
171+
return nil, err
172+
}
173+
174+
out = append(out, res)
175+
}
176+
}
177+
178+
sort.Slice(out, func(i, j int) bool {
179+
if out[i].GetGroupVersionKind() != out[j].GetGroupVersionKind() {
180+
return out[i].GetGroupVersionKind() < out[j].GetGroupVersionKind()
181+
}
182+
183+
return out[i].GetName() < out[j].GetName()
184+
})
185+
186+
return out, nil
187+
}
188+
189+
func (r *ReleaseClient) ListWorkloadsForRelease(namespace, name string) ([]dto.Resource, error) {
190+
resources, err := r.ListResources(namespace, name)
191+
if err != nil {
192+
return nil, err
193+
}
194+
195+
workloads := make([]dto.Resource, 0, 0)
196+
for _, resource := range resources {
197+
if resource.GetGroup() == "apps" &&
198+
resource.GetVersion() == "v1" &&
199+
(resource.GetKind() == "Deployment" || resource.GetKind() == "DaemonSet" || resource.GetKind() == "StatefulSet") {
200+
workloads = append(workloads, resource)
201+
}
202+
}
203+
204+
return workloads, nil
205+
}

cyclops-ctrl/pkg/cluster/k8sclient/client.go

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type IKubernetesClient interface {
8080
DeleteModule(name string) error
8181
GetModule(name string) (*cyclopsv1alpha1.Module, error)
8282
GetResourcesForModule(name string) ([]dto.Resource, error)
83+
MapUnstructuredResource(u unstructured.Unstructured) (dto.Resource, error)
8384
GetWorkloadsForModule(name string) ([]dto.Resource, error)
8485
GetDeletedResources([]dto.Resource, string, string) ([]dto.Resource, error)
8586
GetModuleResourcesHealth(name string) (string, error)

cyclops-ctrl/pkg/cluster/k8sclient/modules.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -94,20 +94,12 @@ func (k *KubernetesClient) GetResourcesForModule(name string) ([]dto.Resource, e
9494
}
9595

9696
for _, o := range other {
97-
status, err := k.getResourceStatus(o)
97+
res, err := k.MapUnstructuredResource(o)
9898
if err != nil {
9999
return nil, err
100100
}
101101

102-
out = append(out, &dto.Other{
103-
Group: o.GroupVersionKind().Group,
104-
Version: o.GroupVersionKind().Version,
105-
Kind: o.GroupVersionKind().Kind,
106-
Name: o.GetName(),
107-
Namespace: o.GetNamespace(),
108-
Status: status,
109-
Deleted: false,
110-
})
102+
out = append(out, res)
111103
}
112104

113105
sort.Slice(out, func(i, j int) bool {
@@ -121,6 +113,23 @@ func (k *KubernetesClient) GetResourcesForModule(name string) ([]dto.Resource, e
121113
return out, nil
122114
}
123115

116+
func (k *KubernetesClient) MapUnstructuredResource(u unstructured.Unstructured) (dto.Resource, error) {
117+
status, err := k.getResourceStatus(u)
118+
if err != nil {
119+
return nil, err
120+
}
121+
122+
return &dto.Other{
123+
Group: u.GroupVersionKind().Group,
124+
Version: u.GroupVersionKind().Version,
125+
Kind: u.GroupVersionKind().Kind,
126+
Name: u.GetName(),
127+
Namespace: u.GetNamespace(),
128+
Status: status,
129+
Deleted: false,
130+
}, nil
131+
}
132+
124133
func (k *KubernetesClient) GetWorkloadsForModule(name string) ([]dto.Resource, error) {
125134
out := make([]dto.Resource, 0, 0)
126135

cyclops-ui/src/components/shared/HelmReleaseDetails/HelmReleaseDetails.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ export const HelmReleaseDetails = ({
266266
useEffect(() => {
267267
if (isStreamingEnabled()) {
268268
resourcesStream(
269-
`/stream/releases/resources/${releaseName}`,
269+
`/stream/releases/${releaseNamespace}/${releaseName}/resources`,
270270
(r: any) => {
271271
let resourceRef: ResourceRef = {
272272
group: r.group,

0 commit comments

Comments
 (0)