Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metrics Feature #7507

Merged
merged 28 commits into from
Apr 12, 2023
Merged

Add metrics Feature #7507

merged 28 commits into from
Apr 12, 2023

Conversation

jweak
Copy link
Contributor

@jweak jweak commented Apr 5, 2023

This PR moves the current metrics implementation to a feature inside packages/core and introduces a new package @k8slens/metrics which exposes injectionTokens for implementing metrics features.

This PR has tokens for following metrics:

  • Cluster overview metrics
  • Metrics in resource details (Cluster, Node, Pod, Container, Deployment, StatefulSet, Ingress, VolumeClaim, ReplicaSet, DaemonSet, Job, Namespace)

Metrics from the core can be included with one line of code in open-lens, registerFeature(metricsFeature). Without registering the feature, Lens will not render metrics where those injectionTokens are used.

Resolves #7506

jweak added 15 commits April 5, 2023 15:59
…Feature

Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
@jweak jweak requested a review from a team as a code owner April 5, 2023 13:16
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
@jweak jweak force-pushed the metrics-extraction branch from 9142744 to afd64df Compare April 5, 2023 13:17
@jweak jweak added enhancement New feature or request area/metrics All the things related to metrics labels Apr 5, 2023
@jweak jweak added this to the 6.5.0 milestone Apr 5, 2023
"name": "@k8slens/metrics",
"private": false,
"version": "6.5.0-alpha.1",
"description": "Code that is common to all Features and those registering them.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be something about metrics?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, copy-pasta! 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
@@ -0,0 +1,4 @@
{
"extends": "@k8slens/typescript/config/base.json",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in the dependencies or does it just work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm good question.. no other package imports this but all of them use it... it should probably be a dev dependency for all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

@@ -0,0 +1 @@
module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in the dependencies or does it just work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

Copy link
Contributor

@jansav jansav Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be added yeah. Hard to remember because they seem to work anyway because of node module resolution and npm workspaces automatically hoisting dependencies to root of monorepo. If we had something more strict, like pnpm then it would be easier to remember.

Copy link
Contributor

@jansav jansav Apr 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #7545 makes it bit more strict to remind us.

);
});

const PodDetailsInitContainers = NonInjectedPodDetailsInitContainers;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for 2 components since we're not injecting anything here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, fixed

jweak added 4 commits April 6, 2023 14:39
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
@jweak
Copy link
Contributor Author

jweak commented Apr 6, 2023

Adding it here so I'll remember to fix this. There's a new warning in the console when navigating to Cluster overview:
Cluster Overview, some other page, back to Cluster Overview

Warning: Cannot update a component (`observerComponent`) while rendering a different component (`observerComponent`). To locate the bad setState() call inside `observerComponent`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
    at observerComponent (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:295016:73)
    at div
    at observerComponent (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:295016:73)
    at div
    at NonInjectedErrorBoundary (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:690197:9)
    at main
    at div
    at observerComponent 

@@ -118,5 +121,6 @@ export const ClusterOverview = withInjectables<Dependencies>(NonInjectedClusterO
podStore: di.inject(podStoreInjectable),
eventStore: di.inject(eventStoreInjectable),
nodeStore: di.inject(nodeStoreInjectable),
uiBlocks: di.injectMany(clusterOverviewUIBlockInjectionToken),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we should use computedInjectMany to make it reactive. Currently it doesn't refresh if new implementation for clusterOverviewUIBlockInjectionToken is registered in runtime. (or deregistered).

This works at the moment because registration of the Feature is happening before anything is rendered, but it's good practice to always support runtime registrations (less work in the future when we enable possibility to register Features through runtime extensions)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, fixed

Comment on lines 9 to 17
export type ClusterOverviewUIBlock = {
id: string;
Component: React.ElementType;
orderNumber: number;
};

export const clusterOverviewUIBlockInjectionToken = getInjectionToken<ClusterOverviewUIBlock>({
id: "cluster-overview-ui-block-injection-token",
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, it seems that these do not belong here. Maybe add a comment to remind that these should be moved under cluster-overview feature whenever it emerges.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a comment here

Comment on lines +28 to +52
export const podDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "pod-details-metrics-injection-token",
});

export const deploymentDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "deployment-details-metrics-injection-token",
});

export const nodeDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "node-details-metrics-injection-token",
});

export const replicaSetDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "replica-set-details-metrics-injection-token",
});

export const persistentVolumeClaimDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "persistent-volume-claim-details-metrics-injection-token",
});

export const statefulSetDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
id: "stateful-set-details-metrics-injection-token",
});

export const namespaceDetailsMetricsInjectionToken = getInjectionToken<KubeObjectDetailMetrics>({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to make all of these bit more generic, so that we don't need multiple injectionTokens for same kind of stuff?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's useful to have separate tokens for each resource type. Maybe someone wants to implement metrics just for one resource but not all?

We don't implement metrics for all resource types ourselves. The other thing is that core decides the placement of the metrics (at least for now), for example Pod details have both Pod metrics and Container metrics where Pod metrics are on top of the details and Container lower in the middle of the details component.

Comment on lines 18 to 21
const metrics = di.injectMany(token);
const first = metrics[0];

const Component = first?.Component ?? EmptyMetrics;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right. If we expect that there's only one item, then we should use di.inject instead of injectMany, but in this case, I think that the correct solution would be to prepare for the fact that there's multiple implementations and render all of them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to use computedMany

@@ -0,0 +1,24 @@
/**
Copy link
Contributor

@jansav jansav Apr 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, the guideline should be that the core doesn't know anything about metrics, therefore the fact that metrics exports *MetricsInjectionToken seems bit odd.

It would make more sense if the kubeObjectDetailItemInjectionToken would be extracted to package and then all of these implementations could also be moved.

Might be that you have already thought it, but it cascaded to something too big?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was deemed to be a too big of an effort right now to move the implementations into a package. It was decided that the old implementations would stay in the core because they are too entangled and the benefit of moving is not that big. We want to make these metrics replaceable but not totally remove them from the core at this point.

@jansav
Copy link
Contributor

jansav commented Apr 11, 2023

Added some comments. Looking good.

jweak added 3 commits April 11, 2023 10:48
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
Signed-off-by: Juho Heikka <juho.heikka@gmail.com>
@jweak
Copy link
Contributor Author

jweak commented Apr 11, 2023

Adding it here so I'll remember to fix this. There's a new warning in the console when navigating to Cluster overview: Cluster Overview, some other page, back to Cluster Overview

Warning: Cannot update a component (`observerComponent`) while rendering a different component (`observerComponent`). To locate the bad setState() call inside `observerComponent`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
    at observerComponent (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:295016:73)
    at div
    at observerComponent (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:295016:73)
    at div
    at NonInjectedErrorBoundary (https://cf2cda344dbade08f9455be5bd0d5662.lens.app:63776/build/lens.js?03198672fc5ca6362ea6:690197:9)
    at main
    at div
    at observerComponent 

can't reproduce this anymore, maybe fixed by merging master / changing overview ui blocks to computed value

@jweak jweak requested a review from jansav April 12, 2023 09:07
@@ -58,5 +73,6 @@ export const metricsFeature = getFeature({

di.register(podDetailsMetricsInjectable);
di.register(podDetailsContainerMetricsInjectable);
di.register(deploymentDetailsMetricsInjectable);
Copy link
Contributor

@Iku-turso Iku-turso Apr 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No action required, this is also possible:

di.register(
  clusterPieChartsClusterOverviewInjectable, 
  clusterMetricsOverviewBlockInjectable, 
  makeMargaritasInjectable, 
  itPutsTheLotionInTheBasketInjectable
);

instantiate: (di) => {
const podMetrics = di.injectMany(podDetailsMetricsInjectionToken);
const first = podMetrics[0];

const Component = first ?? Empty;
const Component = first?.Component ?? EmptyMetrics;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we instead register EmptyMetrics as implementation of podDetailsMetricsInjectionToken when there's nothing better around?


type GetMetricsKubeObjectDetailItem = (token: InjectionToken<KubeObjectDetailMetrics, void>, metricResourceType: ClusterMetricsResourceType) => KubeObjectDetailItem;

export const getMetricsKubeObjectDetailItemInjectable = getInjectable({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be a factory function? Eg. instead of getMetricsKubeObjectDetailItemInjectable, could it be metricsKubeObjectDetailItemInjectable, and receive metricResourceType as its key as keyedSingleton?

@@ -121,6 +122,6 @@ export const ClusterOverview = withInjectables<Dependencies>(NonInjectedClusterO
podStore: di.inject(podStoreInjectable),
eventStore: di.inject(eventStoreInjectable),
nodeStore: di.inject(nodeStoreInjectable),
uiBlocks: di.injectMany(clusterOverviewUIBlockInjectionToken),
uiBlocks: di.inject(computedInjectManyInjectable)(clusterOverviewUIBlockInjectionToken),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shout if you want di.computedInjectMany(...)...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd be useful!

@jweak jweak merged commit 53cb3a5 into master Apr 12, 2023
@jweak jweak deleted the metrics-extraction branch April 12, 2023 11:40
@Nokel81 Nokel81 mentioned this pull request Apr 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/metrics All the things related to metrics enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add metrics feature
5 participants