1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Merge branch 'master' into fix-empty-pod-logs-tab-content

This commit is contained in:
Alex Andreev 2023-02-17 13:03:51 +03:00 committed by GitHub
commit 4c03876448
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 2832 additions and 64 deletions

View File

@ -25,7 +25,7 @@
"devDependencies": { "devDependencies": {
"adr": "^1.4.3", "adr": "^1.4.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"lerna": "^6.5.0", "lerna": "^6.5.1",
"rimraf": "^4.1.2" "rimraf": "^4.1.2"
} }
} }

View File

@ -158,12 +158,12 @@
"hpagent": "^1.2.0", "hpagent": "^1.2.0",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"immer": "^9.0.19", "immer": "^9.0.19",
"joi": "^17.7.0", "joi": "^17.7.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"marked": "^4.2.12", "marked": "^4.2.12",
"md5-file": "^5.0.0", "md5-file": "^5.0.0",
"mobx": "^6.7.0", "mobx": "^6.8.0",
"mobx-observable-history": "^2.0.3", "mobx-observable-history": "^2.0.3",
"mobx-react": "^7.6.0", "mobx-react": "^7.6.0",
"mobx-utils": "^6.0.4", "mobx-utils": "^6.0.4",

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import replicationControllersRouteInjectable from "./replicationcontrollers-route.injectable";
import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token";
const navigateToReplicationControllersInjectable = getInjectable({
id: "navigate-to-replicationcontrollers",
instantiate: (di) => {
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const route = di.inject(replicationControllersRouteInjectable);
return () => navigateToRoute(route);
},
});
export default navigateToReplicationControllersInjectable;

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const replicationControllersRouteInjectable = getInjectable({
id: "replicationcontrollers-route",
instantiate: (di) => ({
path: "/replicationcontrollers",
clusterFrame: true,
isEnabled: di.inject(shouldShowResourceInjectionToken, {
apiName: "replicationcontrollers",
group: "", // core
}),
}),
injectionToken: frontEndRouteInjectionToken,
});
export default replicationControllersRouteInjectable;

View File

@ -33,6 +33,7 @@ export * from "./pod-metrics.api";
export * from "./pod-security-policy.api"; export * from "./pod-security-policy.api";
export * from "./priority-class.api"; export * from "./priority-class.api";
export * from "./replica-set.api"; export * from "./replica-set.api";
export * from "./replication-controller.api";
export * from "./resource-quota.api"; export * from "./resource-quota.api";
export * from "./role.api"; export * from "./role.api";
export * from "./role-binding.api"; export * from "./role-binding.api";

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
import loggerInjectable from "../../logger.injectable";
import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
import { ReplicationControllerApi } from "./replication-controller.api";
const replicationControllerApiInjectable = getInjectable({
id: "replication-controller-api",
instantiate: (di) => {
return new ReplicationControllerApi({
logger: di.inject(loggerInjectable),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
});
},
injectionToken: kubeApiInjectionToken,
});
export default replicationControllerApiInjectable;

View File

@ -0,0 +1,149 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api";
import { KubeApi } from "../kube-api";
import type {
BaseKubeObjectCondition, KubeObjectMetadata,
KubeObjectStatus,
NamespaceScopedMetadata,
} from "../kube-object";
import { KubeObject } from "../kube-object";
import type { PodTemplateSpec } from "./types";
export class ReplicationControllerApi extends KubeApi<ReplicationController> {
constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) {
super(deps, {
...opts ?? {},
objectConstructor: ReplicationController,
});
}
protected getScaleApiUrl(params: { namespace: string; name: string }) {
return `${this.formatUrlForNotListing(params)}/scale`;
}
getScale(params: { namespace: string; name: string }): Promise<Scale> {
return this.request.get(this.getScaleApiUrl(params));
}
scale(params: { namespace: string; name: string }, replicas: number): Promise<Scale> {
return this.request.patch(this.getScaleApiUrl(params), {
data: {
metadata: params,
spec: {
replicas,
},
},
}, {
headers: {
"content-type": "application/strategic-merge-patch+json",
},
});
}
}
export interface Scale {
apiVersion: "autoscaling/v1";
kind: "Scale";
metadata: KubeObjectMetadata;
spec: {
replicas: number;
};
status: {
replicas: number;
selector: string;
};
}
export interface ReplicationControllerSpec {
/**
* Minimum number of seconds for which a newly created pod should be ready without any of its container crashing, for it to be considered available.
* Defaults to 0 (pod will be considered available as soon as it is ready)
*/
minReadySeconds?: number;
/**
* Replicas is the number of desired replicas. This is a pointer to distinguish between explicit zero and unspecified.
* Defaults to 1. More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
*/
replicas?: number;
/**
* Selector is a label query over pods that should match the Replicas count. If Selector is empty, it is defaulted to the labels present on the Pod template.
* Label keys and values that must match in order to be controlled by this replication controller, if empty defaulted to labels on Pod template.
* More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
*/
selector?: Record<string, string>;
/**
* Template is the object that describes the pod that will be created if insufficient replicas are detected. This takes precedence over a TemplateRef.
* More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template
*/
template: PodTemplateSpec;
}
export interface ReplicationControllerStatus extends KubeObjectStatus {
/**
* The number of available replicas (ready for at least minReadySeconds) for this replication controller.
*/
availableReplicas: number;
/**
* The number of pods that have labels matching the labels of the pod template of the replication controller.
*/
fullyLabeledReplicas: number;
/**
* ObservedGeneration reflects the generation of the most recently observed replication controller.
*/
observedGeneration: number;
/**
* The number of ready replicas for this replication controller.
*/
readyReplicas: number;
/**
* Replicas is the most recently observed number of replicas.
* More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#what-is-a-replicationcontroller
*/
replicas: number;
}
export class ReplicationController extends KubeObject<
NamespaceScopedMetadata,
ReplicationControllerStatus,
ReplicationControllerSpec
> {
static kind = "ReplicationController";
static namespaced = true;
static apiBase = "/api/v1/replicationcontrollers";
getMinReadySeconds(): number {
return this.spec?.minReadySeconds ?? 0;
}
getGeneration() {
return this.status?.observedGeneration;
}
getSelectorLabels(): string[] {
return KubeObject.stringifyLabels(this.spec.selector);
}
getReplicas(): number | undefined {
return this.status?.replicas;
}
getDesiredReplicas(): number {
return this.spec?.replicas ?? 0;
}
getAvailableReplicas(): number | undefined {
return this.status?.availableReplicas;
}
getLabeledReplicas(): number | undefined {
return this.status?.fullyLabeledReplicas;
}
getConditions(): BaseKubeObjectCondition[] {
return this.status?.conditions ?? [];
}
}

View File

@ -6,7 +6,7 @@
export type KubeResource = export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" | "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" |
"secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | "secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
"pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" | "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "replicationcontrollers" | "jobs" | "cronjobs" |
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "verticalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "verticalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" |
"priorityclasses" | "runtimeclasses" | "priorityclasses" | "runtimeclasses" |
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts"; "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts";
@ -171,6 +171,11 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
group: "apps", group: "apps",
namespaced: true, namespaced: true,
}, },
replicationcontrollers: {
kind: "ReplicationController",
group: "", // core
namespaced: true,
},
roles: { roles: {
kind: "Role", kind: "Role",
group: "rbac.authorization.k8s.io", group: "rbac.authorization.k8s.io",

View File

@ -27,12 +27,12 @@ export abstract class IpcMain extends IpcRegistrar {
listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
const cleanup = once(() => { const cleanup = once(() => {
this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcMain.removeListener(prefixedChannel, listener); return ipcMain.removeListener(prefixedChannel, listener);
}); });
this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcMain.addListener(prefixedChannel, listener); ipcMain.addListener(prefixedChannel, listener);
this.extension[Disposers].push(cleanup); this.extension[Disposers].push(cleanup);
@ -47,10 +47,10 @@ export abstract class IpcMain extends IpcRegistrar {
handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { handle(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: adding extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcMainHandle(prefixedChannel, handler); ipcMainHandle(prefixedChannel, handler);
this.extension[Disposers].push(() => { this.extension[Disposers].push(() => {
this.extension[lensExtensionDependencies].logger.info(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); this.extension[lensExtensionDependencies].logger.debug(`[IPC-RENDERER]: removing extension handler`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcMain.removeHandler(prefixedChannel); return ipcMain.removeHandler(prefixedChannel);
}); });

View File

@ -28,12 +28,12 @@ export abstract class IpcRenderer extends IpcRegistrar {
listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer {
const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;
const cleanup = once(() => { const cleanup = once(() => {
console.info(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); console.debug(`[IPC-RENDERER]: removing extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
return ipcRenderer.removeListener(prefixedChannel, listener); return ipcRenderer.removeListener(prefixedChannel, listener);
}); });
console.info(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }}); console.debug(`[IPC-RENDERER]: adding extension listener`, { channel, extension: { name: this.extension.name, version: this.extension.version }});
ipcRenderer.addListener(prefixedChannel, listener); ipcRenderer.addListener(prefixedChannel, listener);
this.extension[Disposers].push(cleanup); this.extension[Disposers].push(cleanup);

View File

@ -224,15 +224,96 @@ describe("showing details for helm release", () => {
it("closes details for first release", () => { it("closes details for first release", () => {
expect( expect(
rendered.queryByTestId("helm-release-details-for-some-namespace/some-name"), rendered.queryByTestId(
"helm-release-details-for-some-namespace/some-name",
),
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}); });
it("opens details for second release", () => { it("opens details for second release", () => {
expect( expect(
rendered.getByTestId("helm-release-details-for-some-other-namespace/some-other-name"), rendered.getByTestId(
"helm-release-details-for-some-other-namespace/some-other-name",
),
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
it("shows spinner", () => {
expect(
rendered.getByTestId("helm-release-detail-content-spinner"),
).toBeInTheDocument();
});
describe("when details for second release resolve", () => {
beforeEach(async () => {
await requestDetailedHelmReleaseMock.resolve({
callWasSuccessful: true,
response: {
release: {
appVersion: "some-app-version",
chart: "some-chart-1.0.0",
status: "some-status",
updated: "some-updated",
revision: "some-revision",
name: "some-other-name",
namespace: "some-other-namespace",
},
details: {
name: "some-other-name",
namespace: "some-other-namespace",
version: "some-version",
config: "some-config",
manifest: "some-manifest",
info: {
deleted: "some-deleted",
description: "some-description",
first_deployed: "some-first-deployed",
last_deployed: "some-last-deployed",
notes: "some-notes",
status: "some-status",
},
resources: [
{
kind: "some-kind",
apiVersion: "some-api-version",
metadata: {
uid: "some-uid",
name: "some-resource",
namespace: "some-namespace",
creationTimestamp: "2015-10-22T07:28:00Z",
},
},
],
},
},
});
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("calls for release configuration", () => {
expect(
requestHelmReleaseConfigurationMock,
).toHaveBeenCalledWith("some-other-name", "some-other-namespace", true);
});
describe("when configuration resolves", () => {
beforeEach(async () => {
await requestHelmReleaseConfigurationMock.resolve(
"some-other-configuration",
);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
});
}); });
describe("when details is closed", () => { describe("when details is closed", () => {
@ -410,7 +491,7 @@ describe("showing details for helm release", () => {
describe("when changing the configuration", () => { describe("when changing the configuration", () => {
beforeEach(() => { beforeEach(() => {
const configuration = rendered.getByTestId( const configuration = rendered.getByTestId(
"monaco-editor-for-helm-release-configuration", "monaco-editor-for-helm-release-configuration-some-namespace/some-name",
); );
fireEvent.change(configuration, { fireEvent.change(configuration, {
@ -424,7 +505,7 @@ describe("showing details for helm release", () => {
it("has the configuration", () => { it("has the configuration", () => {
const input = rendered.getByTestId( const input = rendered.getByTestId(
"monaco-editor-for-helm-release-configuration", "monaco-editor-for-helm-release-configuration-some-namespace/some-name",
); );
expect(input).toHaveValue("some-new-configuration"); expect(input).toHaveValue("some-new-configuration");
@ -466,7 +547,7 @@ describe("showing details for helm release", () => {
it("overrides the user inputted configuration with new configuration", () => { it("overrides the user inputted configuration with new configuration", () => {
const input = rendered.getByTestId( const input = rendered.getByTestId(
"monaco-editor-for-helm-release-configuration", "monaco-editor-for-helm-release-configuration-some-namespace/some-name",
); );
expect(input).toHaveValue("some-other-configuration"); expect(input).toHaveValue("some-other-configuration");

View File

@ -27,10 +27,11 @@ export interface UrlSource {
} }
export type ContentSource = RequireExactlyOne<FileSource & UrlSource>; export type ContentSource = RequireExactlyOne<FileSource & UrlSource>;
// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc
enum ChromiumNetError { enum ChromiumNetError {
SUCCESS = 0, SUCCESS = 0,
FAILURE = 1, FAILURE = -2,
RESULT_FROM_CHROMIUM, RESULT_FROM_CHROMIUM = -3,
} }
export interface ElectronWindowConfiguration { export interface ElectronWindowConfiguration {

View File

@ -85,6 +85,7 @@ const NonInjectedReleaseDetailsContent = observer(({ model }: Dependencies & Rel
</DrawerItem> </DrawerItem>
<ReleaseValues <ReleaseValues
releaseId={model.id}
configuration={model.configuration} configuration={model.configuration}
onlyUserSuppliedValuesAreShown={ onlyUserSuppliedValuesAreShown={
model.onlyUserSuppliedValuesAreShown model.onlyUserSuppliedValuesAreShown
@ -150,11 +151,12 @@ const ResourceGroup = ({
); );
interface ReleaseValuesProps { interface ReleaseValuesProps {
releaseId: string;
configuration: ConfigurationInput; configuration: ConfigurationInput;
onlyUserSuppliedValuesAreShown: OnlyUserSuppliedValuesAreShownToggle; onlyUserSuppliedValuesAreShown: OnlyUserSuppliedValuesAreShownToggle;
} }
const ReleaseValues = observer(({ configuration, onlyUserSuppliedValuesAreShown }: ReleaseValuesProps) => { const ReleaseValues = observer(({ releaseId, configuration, onlyUserSuppliedValuesAreShown }: ReleaseValuesProps) => {
const configurationIsLoading = configuration.isLoading.get(); const configurationIsLoading = configuration.isLoading.get();
return ( return (
@ -171,7 +173,7 @@ const ReleaseValues = observer(({ configuration, onlyUserSuppliedValuesAreShown
/> />
<MonacoEditor <MonacoEditor
id="helm-release-configuration" id={`helm-release-configuration-${releaseId}`}
style={{ minHeight: 300 }} style={{ minHeight: 300 }}
value={configuration.nonSavedValue.get()} value={configuration.nonSavedValue.get()}
onChange={configuration.onChange} onChange={configuration.onChange}

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export * from "./replicationcontrollers";
export * from "./replicationcontroller-details";

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { ReplicationControllers } from "./replicationcontrollers";
import {
routeSpecificComponentInjectionToken,
} from "../../routes/route-specific-component-injection-token";
import replicationControllersRouteInjectable
from "../../../common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable";
const replicationControllersRouteComponentInjectable = getInjectable({
id: "replicationcontroller-route-component",
instantiate: (di) => ({
route: di.inject(replicationControllersRouteInjectable),
Component: ReplicationControllers,
}),
injectionToken: routeSpecificComponentInjectionToken,
});
export default replicationControllersRouteComponentInjectable;

View File

@ -0,0 +1,16 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
.ReplicationControllerDetails {
.replicas {
display: flex;
gap: calc(var(--margin) * 2);
align-items: center;
> * {
flex-shrink: 0;
}
}
}

View File

@ -0,0 +1,122 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./replicationcontroller-details.module.scss";
import React from "react";
import { action, makeObservable, observable } from "mobx";
import { observer } from "mobx-react";
import { withInjectables } from "@ogre-tools/injectable-react";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import type {
ReplicationController,
ReplicationControllerApi,
} from "../../../common/k8s-api/endpoints";
import replicationControllerApiInjectable
from "../../../common/k8s-api/endpoints/replication-controller.api.injectable";
import showErrorNotificationInjectable from "../notifications/show-error-notification.injectable";
import type { ShowNotification } from "../notifications";
import { Slider } from "../slider";
export interface ReplicationControllerDetailsProps extends KubeObjectDetailsProps<ReplicationController> {
}
interface Dependencies {
api: ReplicationControllerApi;
showNotificationError: ShowNotification;
}
@observer
class NonInjectedReplicationControllerDetails<Props extends ReplicationControllerDetailsProps & Dependencies> extends React.Component<Props> {
@observable sliderReplicasValue = this.props.object.getDesiredReplicas();
@observable sliderReplicasDisabled = false;
constructor(props: Props) {
super(props);
makeObservable(this);
}
@action
async scale(replicas: number) {
const { object: resource, api, showNotificationError } = this.props;
try {
await api.scale({
name: resource.getName(),
namespace: resource.getNs(),
}, replicas);
} catch (error) {
this.sliderReplicasValue = resource.getDesiredReplicas(); // rollback to last valid value
showNotificationError(error as Error);
}
}
@action
async onScaleSliderChangeCommitted(evt: React.FormEvent<any>, replicas: number) {
this.sliderReplicasDisabled = true;
await this.scale(replicas);
this.sliderReplicasDisabled = false;
}
render() {
const { object: resource } = this.props;
return (
<div className={styles.ReplicationControllerDetails}>
<DrawerTitle>
Spec
</DrawerTitle>
<DrawerItem name="Replicas">
<div className={styles.replicas}>
<div>{resource.getDesiredReplicas()}</div>
<div>Scale</div>
<Slider
min={0}
max={100}
valueLabelDisplay="auto"
disabled={this.sliderReplicasDisabled}
value={this.sliderReplicasValue}
onChange={(evt, value) => this.sliderReplicasValue = value}
onChangeCommitted={(event, value) => this.onScaleSliderChangeCommitted(event, value as number)}
/>
</div>
</DrawerItem>
<DrawerItem name="Selectors" labelsOnly>
{
resource.getSelectorLabels().map(label => (<Badge key={label} label={label} />))
}
</DrawerItem>
<DrawerTitle>
Status
</DrawerTitle>
<DrawerItem name="Replicas">
{resource.getReplicas()}
</DrawerItem>
<DrawerItem name="Available Replicas">
{resource.getAvailableReplicas()}
</DrawerItem>
<DrawerItem name="Labeled Replicas">
{resource.getLabeledReplicas()}
</DrawerItem>
<DrawerItem name="Controller Generation">
{resource.getGeneration()}
</DrawerItem>
<DrawerItem name="Minimum Pod Readiness">
{`${resource.getMinReadySeconds()} seconds`}
</DrawerItem>
</div>
);
}
}
export const ReplicationControllerDetails = withInjectables<Dependencies, ReplicationControllerDetailsProps>(NonInjectedReplicationControllerDetails, {
getProps: (di, props) => ({
...props,
api: di.inject(replicationControllerApiInjectable),
showNotificationError: di.inject(showErrorNotificationInjectable),
}),
});

View File

@ -0,0 +1,39 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { workloadsSidebarItemId } from "../+workloads/workloads-sidebar-items.injectable";
import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable";
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
import replicationControllersRouteInjectable
from "../../../common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable";
import navigateToReplicationControllersInjectable
from "../../../common/front-end-routing/routes/cluster/workloads/replicationcontrollers/navigate-to-replication-controllers.injectable";
const replicationControllerSidebarItemsInjectable = getInjectable({
id: "replicationctrl-sidebar-items",
instantiate: (di) => {
const route = di.inject(replicationControllersRouteInjectable);
const navigateToPage = di.inject(navigateToReplicationControllersInjectable);
const routeIsActive = di.inject(routeIsActiveInjectable, route);
return computed(() => [
{
id: "replication-controllers",
parentId: workloadsSidebarItemId,
title: "Replication Controllers",
onClick: navigateToPage,
isActive: routeIsActive,
isVisible: route.isEnabled,
orderNumber: 61,
},
]);
},
injectionToken: sidebarItemsInjectionToken,
});
export default replicationControllerSidebarItemsInjectable;

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
import { ReplicationControllerStore } from "./replicationcontroller-store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import replicationControllerApiInjectable
from "../../../common/k8s-api/endpoints/replication-controller.api.injectable";
const replicationControllerStoreInjectable = getInjectable({
id: "replication-controller-store",
instantiate: (di) => {
const api = di.inject(replicationControllerApiInjectable);
return new ReplicationControllerStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api);
},
injectionToken: kubeObjectStoreInjectionToken,
});
export default replicationControllerStoreInjectable;

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type {
ReplicationController,
ReplicationControllerApi,
} from "../../../common/k8s-api/endpoints";
import type {
KubeObjectStoreDependencies,
KubeObjectStoreOptions,
} from "../../../common/k8s-api/kube-object.store";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
export interface ReplicationControllerStoreDependencies extends KubeObjectStoreDependencies {
}
export class ReplicationControllerStore extends KubeObjectStore<ReplicationController, ReplicationControllerApi> {
constructor(protected readonly dependencies: ReplicationControllerStoreDependencies, api: ReplicationControllerApi, opts?: KubeObjectStoreOptions) {
super(dependencies, api, opts);
}
}

View File

@ -0,0 +1,8 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
.ReplicationControllers {
}

View File

@ -0,0 +1,86 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./replicationcontrollers.module.scss";
import React from "react";
import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout";
import type { ReplicationControllerStore } from "./replicationcontroller-store";
import { withInjectables } from "@ogre-tools/injectable-react";
import replicationControllerStoreInjectable from "./replicationcontroller-store.injectable";
import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge";
import { Badge } from "../badge";
enum columnId {
name = "name",
namespace = "namespace",
replicas = "replicas",
replicasDesired = "replicasDesired",
selector = "selector",
}
interface Dependencies {
store: ReplicationControllerStore;
}
const NonInjectedReplicationControllers = observer((props: Dependencies) => {
return (
<SiblingsInTabLayout>
<KubeObjectListLayout
isConfigurable
tableId="workload_replicationcontrollers"
className={styles.ReplicationControllers}
store={props.store}
sortingCallbacks={{
[columnId.name]: item => item.getName(),
[columnId.namespace]: item => item.getNs(),
[columnId.selector]: item => item.getSelectorLabels(),
[columnId.replicas]: item => item.getReplicas(),
[columnId.replicasDesired]: item => item.getDesiredReplicas(),
}}
searchFilters={[
item => item.getSearchFields(),
item => item.getSelectorLabels(),
]}
renderHeaderTitle="Replication Controllers"
renderTableHeader={[
{ title: "Name", className: "name", sortBy: columnId.name, id: columnId.name },
{
title: "Namespace",
className: "namespace",
sortBy: columnId.namespace,
id: columnId.namespace,
},
{ title: "Replicas", sortBy: columnId.replicas, id: columnId.replicas },
{
title: "Desired Replicas",
sortBy: columnId.replicasDesired,
id: columnId.replicasDesired,
},
{
title: "Selector",
sortBy: columnId.selector,
id: columnId.selector,
},
]}
renderTableContents={item => [
item.getName(),
<NamespaceSelectBadge key="namespace" namespace={item.getNs()} />,
item.getReplicas(),
item.getDesiredReplicas(),
item.getSelectorLabels().map(label => (<Badge key={label} label={label} />)),
]}
/>
</SiblingsInTabLayout>
);
});
export const ReplicationControllers = withInjectables<Dependencies>(NonInjectedReplicationControllers, {
getProps: (di, props) => ({
...props,
store: di.inject(replicationControllerStoreInjectable),
}),
});

View File

@ -0,0 +1,35 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token";
import { computed } from "mobx";
import {
kubeObjectMatchesToKindAndApiVersion,
} from "../kube-object-matches-to-kind-and-api-version";
import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable";
import { ReplicationControllerDetails } from "../../../+workloads-replicationcontrollers";
const replicationControllerDetailItemInjectable = getInjectable({
id: "replication-controller-detail-item",
instantiate(di) {
const kubeObject = di.inject(currentKubeObjectInDetailsInjectable);
return {
Component: ReplicationControllerDetails,
enabled: computed(() => isReplicationController(kubeObject.value.get()?.object)),
orderNumber: 10,
};
},
injectionToken: kubeObjectDetailItemInjectionToken,
});
export const isReplicationController = kubeObjectMatchesToKindAndApiVersion(
"ReplicationController",
["v1"],
);
export default replicationControllerDetailItemInjectable;

View File

@ -27,6 +27,7 @@ export const ResourceNames: Record<KubeResource, string> = {
"deployments": "Deployments", "deployments": "Deployments",
"statefulsets": "Stateful Sets", "statefulsets": "Stateful Sets",
"replicasets": "Replica Sets", "replicasets": "Replica Sets",
"replicationcontrollers": "Replication Controllers",
"jobs": "Jobs", "jobs": "Jobs",
"cronjobs": "Cron Jobs", "cronjobs": "Cron Jobs",
"endpoints": "Endpoints", "endpoints": "Endpoints",

View File

@ -1778,10 +1778,10 @@
dependencies: dependencies:
"@hapi/hoek" "^9.0.0" "@hapi/hoek" "^9.0.0"
"@sideway/formula@^3.0.0": "@sideway/formula@^3.0.1":
version "3.0.0" version "3.0.1"
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
"@sideway/pinpoint@^2.0.0": "@sideway/pinpoint@^2.0.0":
version "2.0.0" version "2.0.0"
@ -7806,15 +7806,15 @@ jest@^28.1.3:
import-local "^3.0.2" import-local "^3.0.2"
jest-cli "^28.1.3" jest-cli "^28.1.3"
joi@^17.7.0: joi@^17.7.1:
version "17.7.0" version "17.7.1"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.1.tgz#854fc85c7fa3cfc47c91124d30bffdbb58e06cec"
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== integrity sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==
dependencies: dependencies:
"@hapi/hoek" "^9.0.0" "@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0" "@hapi/topo" "^5.0.0"
"@sideway/address" "^4.1.3" "@sideway/address" "^4.1.3"
"@sideway/formula" "^3.0.0" "@sideway/formula" "^3.0.1"
"@sideway/pinpoint" "^2.0.0" "@sideway/pinpoint" "^2.0.0"
jose@^4.10.0: jose@^4.10.0:
@ -8941,10 +8941,10 @@ mobx-utils@^6.0.4:
resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.5.tgz#0cce9afb07fbba1fb559f959f8cea1f44baa7252" resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.5.tgz#0cce9afb07fbba1fb559f959f8cea1f44baa7252"
integrity sha512-QOduwicYedD4mwYZRl8+c3BalljFDcubg+PUGqBkn8tOuBoj2q7GhjXBP6JXM9J+Zh+2mePK8IoToeLfqr3Z/w== integrity sha512-QOduwicYedD4mwYZRl8+c3BalljFDcubg+PUGqBkn8tOuBoj2q7GhjXBP6JXM9J+Zh+2mePK8IoToeLfqr3Z/w==
mobx@^6.3.0, mobx@^6.7.0: mobx@^6.3.0, mobx@^6.8.0:
version "6.7.0" version "6.8.0"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.7.0.tgz#2d805610fee1801fd015c54fd5400d2601aa3768" resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.8.0.tgz#59051755fdb5c8a9f3f2e0a9b6abaf86bab7f843"
integrity sha512-1kBLBdSNG2bA522HQdbsTvwAwYf9hq9FWxmlhX7wTsJUAI54907J+ozfGW+LoYUo06vjit748g6QH1AAGLNebw== integrity sha512-+o/DrHa4zykFMSKfS8Z+CPSEg5LW9tSNGTuN8o6MF1GKxlfkSHSeJn5UtgxvPkGgaouplnrLXCF+duAsmm6FHQ==
mock-http@^1.1.0: mock-http@^1.1.0:
version "1.1.0" version "1.1.0"

View File

@ -28,7 +28,7 @@
"gunzip-maybe": "^1.4.2", "gunzip-maybe": "^1.4.2",
"node-fetch": "^3.3.0", "node-fetch": "^3.3.0",
"tar-stream": "^3.0.0", "tar-stream": "^3.0.0",
"zod": "^3.20.2" "zod": "^3.20.6"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.61", "@swc/cli": "^0.1.61",

View File

@ -1206,7 +1206,7 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
zod@^3.20.2: zod@^3.20.6:
version "3.20.2" version "3.20.6"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807" resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.6.tgz#2f2f08ff81291d47d99e86140fedb4e0db08361a"
integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ== integrity sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==

View File

@ -200,7 +200,7 @@
"@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1",
"@ogre-tools/injectable-extension-for-mobx": "^12.0.1", "@ogre-tools/injectable-extension-for-mobx": "^12.0.1",
"@ogre-tools/injectable-react": "^12.0.1", "@ogre-tools/injectable-react": "^12.0.1",
"mobx": "^6.7.0", "mobx": "^6.8.0",
"rimraf": "^4.1.2" "rimraf": "^4.1.2"
}, },
"devDependencies": { "devDependencies": {

View File

@ -7451,10 +7451,10 @@ mobx-utils@^6.0.4:
resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.5.tgz#0cce9afb07fbba1fb559f959f8cea1f44baa7252" resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.5.tgz#0cce9afb07fbba1fb559f959f8cea1f44baa7252"
integrity sha512-QOduwicYedD4mwYZRl8+c3BalljFDcubg+PUGqBkn8tOuBoj2q7GhjXBP6JXM9J+Zh+2mePK8IoToeLfqr3Z/w== integrity sha512-QOduwicYedD4mwYZRl8+c3BalljFDcubg+PUGqBkn8tOuBoj2q7GhjXBP6JXM9J+Zh+2mePK8IoToeLfqr3Z/w==
mobx@^6.3.0, mobx@^6.7.0: mobx@^6.3.0, mobx@^6.7.0, mobx@^6.8.0:
version "6.7.0" version "6.8.0"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.7.0.tgz#2d805610fee1801fd015c54fd5400d2601aa3768" resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.8.0.tgz#59051755fdb5c8a9f3f2e0a9b6abaf86bab7f843"
integrity sha512-1kBLBdSNG2bA522HQdbsTvwAwYf9hq9FWxmlhX7wTsJUAI54907J+ozfGW+LoYUo06vjit748g6QH1AAGLNebw== integrity sha512-+o/DrHa4zykFMSKfS8Z+CPSEg5LW9tSNGTuN8o6MF1GKxlfkSHSeJn5UtgxvPkGgaouplnrLXCF+duAsmm6FHQ==
moment-timezone@^0.5.40: moment-timezone@^0.5.40:
version "0.5.40" version "0.5.40"

View File

@ -38,21 +38,21 @@
resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b"
integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==
"@lerna/child-process@6.5.0": "@lerna/child-process@6.5.1":
version "6.5.0" version "6.5.1"
resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.0.tgz#8a0d37453f2163c3a1737493d328399a53e4cc5b" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-6.5.1.tgz#da9161ba00e8d67fa7241a709703e5cc5e4a5e5e"
integrity sha512-ZuN3eivyzkaCCT4MNwHW5FuJ0Zu4kPFCnx7NXcGisca4a7Urjs3odZN1Tf9ZoYcPCf2I9DKfHj2bfnS0SHpMIg== integrity sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw==
dependencies: dependencies:
chalk "^4.1.0" chalk "^4.1.0"
execa "^5.0.0" execa "^5.0.0"
strong-log-transformer "^2.1.0" strong-log-transformer "^2.1.0"
"@lerna/create@6.5.0": "@lerna/create@6.5.1":
version "6.5.0" version "6.5.1"
resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.0.tgz#b8350e145968f17c4b785c50c0cba49079c192e8" resolved "https://registry.yarnpkg.com/@lerna/create/-/create-6.5.1.tgz#326b5d26c247bfc9e2d8728aa1f69419840cec8c"
integrity sha512-EbYXW6W/khXDXhA15HETcU7toq2zxuqalBw9MZzHWj3sEBQsTd/un56qzIRMWUuHy8CO7FxJIdPItYI8QPop4A== integrity sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw==
dependencies: dependencies:
"@lerna/child-process" "6.5.0" "@lerna/child-process" "6.5.1"
dedent "^0.7.0" dedent "^0.7.0"
fs-extra "^9.1.0" fs-extra "^9.1.0"
init-package-json "^3.0.2" init-package-json "^3.0.2"
@ -2266,7 +2266,7 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@4.1.0: js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
@ -2359,13 +2359,13 @@ lazy-cache@^2.0.2:
dependencies: dependencies:
set-getter "^0.1.0" set-getter "^0.1.0"
lerna@^6.5.0: lerna@^6.5.1:
version "6.5.0" version "6.5.1"
resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.0.tgz#c46f179ecf88d6a55010ced0a0ccdacdf9d3fcd2" resolved "https://registry.yarnpkg.com/lerna/-/lerna-6.5.1.tgz#eb89698e5b2891f5681f39d980f63d0519fc464f"
integrity sha512-2GbRXeQLFf9KwxRXd+AgEEzKat4DbvVnXtSVDkpgzIzITXtYPThxqzYhhaHUhBSM9Rq7TcJ1XLbArS2qIvlazg== integrity sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA==
dependencies: dependencies:
"@lerna/child-process" "6.5.0" "@lerna/child-process" "6.5.1"
"@lerna/create" "6.5.0" "@lerna/create" "6.5.1"
"@npmcli/arborist" "5.3.0" "@npmcli/arborist" "5.3.0"
"@npmcli/run-script" "4.1.7" "@npmcli/run-script" "4.1.7"
"@nrwl/devkit" ">=15.5.2 < 16" "@nrwl/devkit" ">=15.5.2 < 16"
@ -2398,6 +2398,7 @@ lerna@^6.5.0:
inquirer "^8.2.4" inquirer "^8.2.4"
is-ci "2.0.0" is-ci "2.0.0"
is-stream "2.0.0" is-stream "2.0.0"
js-yaml "^4.1.0"
libnpmaccess "6.0.3" libnpmaccess "6.0.3"
libnpmpublish "6.0.4" libnpmpublish "6.0.4"
load-json-file "6.2.0" load-json-file "6.2.0"