diff --git a/core/server/helm_release.go b/core/server/helm_release.go index 0d85684ee9..9340f04f4f 100644 --- a/core/server/helm_release.go +++ b/core/server/helm_release.go @@ -1,81 +1,25 @@ package server import ( - "bytes" - "compress/gzip" "context" - "encoding/base64" - "encoding/json" "fmt" - "io" "strings" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" - "github.com/fluxcd/pkg/ssa" "github.com/weaveworks/weave-gitops/core/clustersmngr" - "github.com/weaveworks/weave-gitops/core/server/types" pb "github.com/weaveworks/weave-gitops/pkg/api/core" - v1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" ) func getHelmReleaseInventory(ctx context.Context, helmRelease helmv2.HelmRelease, c clustersmngr.Client, cluster string) ([]*pb.GroupVersionKind, error) { - storageNamespace := helmRelease.GetStorageNamespace() - - storageName := helmRelease.GetReleaseName() - - storageVersion := helmRelease.Status.LastReleaseRevision - if storageVersion < 1 { - // skip release if it failed to install - return nil, nil - } - - storageSecret := &v1.Secret{} - secretName := fmt.Sprintf("sh.helm.release.v1.%s.v%v", storageName, storageVersion) - key := client.ObjectKey{ - Name: secretName, - Namespace: storageNamespace, - } - - if err := c.Get(ctx, cluster, key, storageSecret); err != nil { - return nil, err - } - - releaseData, releaseFound := storageSecret.Data["release"] - if !releaseFound { - return nil, fmt.Errorf("failed to decode the Helm storage object for HelmRelease '%s'", helmRelease.Name) - } - - byteData, err := base64.StdEncoding.DecodeString(string(releaseData)) + k8sClient, err := c.Scoped(cluster) if err != nil { - return nil, err - } - - var magicGzip = []byte{0x1f, 0x8b, 0x08} - if bytes.Equal(byteData[0:3], magicGzip) { - r, err := gzip.NewReader(bytes.NewReader(byteData)) - if err != nil { - return nil, err - } - - defer r.Close() - - uncompressedByteData, err := io.ReadAll(r) - if err != nil { - return nil, err - } - - byteData = uncompressedByteData + return nil, fmt.Errorf("error getting scoped client for cluster=%s: %w", cluster, err) } - storage := types.HelmReleaseStorage{} - if err := json.Unmarshal(byteData, &storage); err != nil { - return nil, fmt.Errorf("failed to decode the Helm storage object for HelmRelease '%s': %w", helmRelease.Name, err) - } + objects, err := getHelmReleaseObjects(ctx, k8sClient, &helmRelease) - objects, err := ssa.ReadObjects(strings.NewReader(storage.Manifest)) if err != nil { - return nil, fmt.Errorf("failed to read the Helm storage object for HelmRelease '%s': %w", helmRelease.Name, err) + return nil, err } var gvk []*pb.GroupVersionKind diff --git a/core/server/inventory.go b/core/server/inventory.go index 519129dcfb..62215a9e5b 100644 --- a/core/server/inventory.go +++ b/core/server/inventory.go @@ -195,6 +195,11 @@ func getHelmReleaseObjects(ctx context.Context, k8sClient client.Client, helmRel Namespace: storageNamespace, } + if helmRelease.Spec.KubeConfig != nil { + // helmrelease secret is on another cluster so we cannot inspect it to figure out the inventory and version and other things + return nil, nil + } + if err := k8sClient.Get(ctx, key, storageSecret); err != nil { return nil, err } diff --git a/core/server/inventory_test.go b/core/server/inventory_test.go index fe07ef165d..16cc1138b6 100644 --- a/core/server/inventory_test.go +++ b/core/server/inventory_test.go @@ -9,6 +9,7 @@ import ( helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" + "github.com/fluxcd/pkg/apis/meta" sourcev1 "github.com/fluxcd/source-controller/api/v1" . "github.com/onsi/gomega" "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" @@ -270,3 +271,49 @@ func TestGetInventoryHelmRelease(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) g.Expect(res.Entries).To(HaveLen(1)) } + +func TestGetInventoryHelmReleaseWithKubeconfig(t *testing.T) { + g := NewGomegaWithT(t) + + scheme, err := kube.CreateScheme() + g.Expect(err).NotTo(HaveOccurred()) + + ctx := context.Background() + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + helm1 := &helmv2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "first-helm-name", + Namespace: ns.Name, + }, + Spec: helmv2.HelmReleaseSpec{ + KubeConfig: &meta.KubeConfigReference{ + SecretRef: meta.SecretKeyReference{ + Name: "kubeconfig", + }, + }, + }, + Status: helmv2.HelmReleaseStatus{ + LastReleaseRevision: 1, + }, + } + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(ns, helm1).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.GetInventory(ctx, &pb.GetInventoryRequest{ + Namespace: ns.Name, + ClusterName: cluster.DefaultCluster, + Kind: "HelmRelease", + Name: helm1.Name, + WithChildren: true, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Entries).To(HaveLen(0)) +} diff --git a/ui/components/Alert.tsx b/ui/components/Alert.tsx index c3e49ab0af..215d2135a3 100644 --- a/ui/components/Alert.tsx +++ b/ui/components/Alert.tsx @@ -1,6 +1,7 @@ import { Alert as MaterialAlert, AlertTitle } from "@material-ui/lab"; import * as React from "react"; import styled from "styled-components"; +import { ThemeTypes } from "../contexts/AppContext"; import Flex from "./Flex"; import Icon, { IconType } from "./Icon"; import Text from "./Text"; @@ -23,9 +24,7 @@ function UnstyledAlert({ center, title, message, severity, className }: Props) { return ( - } + icon={} severity={severity} > {title} @@ -37,11 +36,23 @@ function UnstyledAlert({ center, title, message, severity, className }: Props) { const Alert = styled(UnstyledAlert)` .MuiAlert-standardError { + svg { + color: ${(props) => props.theme.colors.alertDark}; + } background-color: ${(props) => props.theme.colors.alertLight}; } .MuiAlertTitle-root { color: ${(props) => props.theme.colors.black}; } + .MuiAlert-standardInfo { + svg { + color: ${(props) => props.theme.colors.primary10}; + } + background-color: ${(props) => + props.theme.mode === ThemeTypes.Dark + ? props.theme.colors.primary20 + : null}; + } `; export default Alert; diff --git a/ui/components/AutomationDetail.tsx b/ui/components/AutomationDetail.tsx index d15f0e1fc4..9dec01033b 100644 --- a/ui/components/AutomationDetail.tsx +++ b/ui/components/AutomationDetail.tsx @@ -5,6 +5,7 @@ import { createCanaryCondition, useGetInventory } from "../hooks/inventory"; import { Condition, Kind, ObjectRef } from "../lib/api/core/types.pb"; import { Automation, HelmRelease } from "../lib/objects"; import { automationLastUpdated } from "../lib/utils"; +import Alert from "./Alert"; import Collapsible from "./Collapsible"; import DependenciesView from "./DependenciesView"; import EventsTable from "./EventsTable"; @@ -25,6 +26,9 @@ import Text from "./Text"; import Timestamp from "./Timestamp"; import YamlView from "./YamlView"; +const hrInfoMessage = + "spec.Kubeconfig is set on this HelmRelease. Details about reconciled objects are not available."; + type Props = { automation: Automation; className?: string; @@ -87,10 +91,15 @@ function AutomationDetail({ component: () => { return ( - + {automation.type === "HelmRelease" && + (automation as HelmRelease).kubeConfig === "" ? ( + + ) : ( + + )} ); }, diff --git a/ui/lib/objects.ts b/ui/lib/objects.ts index e7d5185902..af6af25b45 100644 --- a/ui/lib/objects.ts +++ b/ui/lib/objects.ts @@ -301,6 +301,10 @@ export class HelmRelease extends FluxObject { get lastAttemptedRevision(): string { return this.obj.status?.lastAttemptedRevision || ""; } + + get kubeConfig(): string { + return this.obj.spec?.kubeConfig?.secretRef?.name || ""; + } } export class Provider extends FluxObject {