Compare commits

...

5 Commits

Author SHA1 Message Date
Prabhat Khera
8fc6fdff17 fix namespace refresh flag issue
Some checks are pending
Test / test-client (push) Waiting to run
2023-03-13 09:30:01 +13:00
Prabhat Khera
4beedb5f69 refresh namespace cache on namespace datatable view 2023-03-10 10:02:49 +13:00
andres-portainer
c609f6912f fix(home): disable live connect for async [EE-5000] (#8628) 2023-03-09 15:50:36 -03:00
Ali
346fe9e3f1 refactor(GPU): colocate and update UI [EE-5127] (#8634)
Co-authored-by: testa113 <testa113>
2023-03-09 22:06:49 +13:00
matias-portainer
69f14e569b fix(stacks): pass WorkingDir to deployer command EE-5142 (#8624) 2023-03-08 19:34:50 -03:00
18 changed files with 87 additions and 77 deletions

View File

@@ -82,9 +82,11 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
}
err = manager.deployer.Remove(ctx, stack.Name, nil, libstack.Options{
WorkingDir: stack.ProjectPath,
EnvFilePath: envFilePath,
Host: url,
})
return errors.Wrap(err, "failed to remove a stack")
}

View File

@@ -1,6 +1,7 @@
import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withControlledInput } from '@/react-tools/withControlledInput';
import { StackContainersDatatable } from '@/react/docker/stacks/ItemView/StackContainersDatatable';
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
import { TemplateListDropdownAngular } from '@/react/docker/app-templates/TemplateListDropdown';
@@ -11,6 +12,8 @@ import { withReactQuery } from '@/react-tools/withReactQuery';
import { withUIRouter } from '@/react-tools/withUIRouter';
import { DockerfileDetails } from '@/react/docker/images/ItemView/DockerfileDetails';
import { HealthStatus } from '@/react/docker/containers/ItemView/HealthStatus';
import { GpusList } from '@/react/docker/host/SetupView/GpusList';
import { GpusInsights } from '@/react/docker/host/SetupView/GpusInsights';
export const componentsModule = angular
.module('portainer.docker.react.components', [])
@@ -45,4 +48,9 @@ export const componentsModule = angular
'usedAllGpus',
'enableGpuManagement',
])
).name;
)
.component(
'gpusList',
r2a(withControlledInput(GpusList), ['value', 'onChange'])
)
.component('gpusInsights', r2a(GpusInsights, [])).name;

View File

@@ -151,21 +151,7 @@
<div class="col-sm-12 form-section-title"> Other </div>
<div class="form-group">
<div class="col-sm-12 pb-3">
<insights-box
header="'GPU settings update'"
set-html-content="true"
insight-close-id="'gpu-settings-update-closed'"
content="'
<p>
From 2.18 on, the set-up of available GPUs for a Docker Standalone environment has been shifted from Add environment and Environment details to Host -> Setup, so as to align with other settings.
</p>
<p>
A toggle has been introduced for enabling/disabling management of GPU settings in the Portainer UI - to alleviate the performance impact of showing those settings.
</p>
<p>
The UI has been updated to clarify that GPU settings support is only for Docker Standalone (and not Docker Swarm, which was never supported in the UI).
</p>'"
></insights-box>
<gpus-insights></gpus-insights>
</div>
<div class="col-sm-12">
<por-switch-field

View File

@@ -141,7 +141,9 @@ export default class HelmTemplatesController {
try {
const resourcePools = await this.KubernetesResourcePoolService.get();
const nonSystemNamespaces = resourcePools.filter((resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
const nonSystemNamespaces = resourcePools.filter(
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && resourcePool.Namespace.Status === 'Active'
);
this.state.resourcePools = _.sortBy(nonSystemNamespaces, ({ Namespace }) => (Namespace.Name === 'default' ? 0 : 1));
this.state.resourcePool = this.state.resourcePools[0];
} catch (err) {

View File

@@ -81,18 +81,19 @@ class KubernetesNamespaceService {
}
}
async get(name) {
async get(name, refreshCache = false) {
if (name) {
return this.$async(this.getAsync, name);
}
const cachedAllowedNamespaces = this.LocalStorage.getAllowedNamespaces();
if (cachedAllowedNamespaces) {
updateNamespaces(cachedAllowedNamespaces);
return cachedAllowedNamespaces;
} else {
if (!cachedAllowedNamespaces || refreshCache) {
const allowedNamespaces = await this.getAllAsync();
this.LocalStorage.storeAllowedNamespaces(allowedNamespaces);
updateNamespaces(allowedNamespaces);
return allowedNamespaces;
} else {
updateNamespaces(cachedAllowedNamespaces);
return cachedAllowedNamespaces;
}
}

View File

@@ -36,8 +36,8 @@ export function KubernetesResourcePoolService(
}
// getting the quota for all namespaces is costly by default, so disable getting it by default
async function getAll({ getQuota = false }) {
const namespaces = await KubernetesNamespaceService.get();
async function getAll({ getQuota = false, refreshCache = false }) {
const namespaces = await KubernetesNamespaceService.get('', refreshCache);
const pools = await Promise.all(
_.map(namespaces, async (namespace) => {
const name = namespace.Name;

View File

@@ -189,7 +189,7 @@ class KubernetesApplicationsController {
};
this.state.namespaces = await this.KubernetesNamespaceService.get();
this.state.namespaces = this.state.namespaces.filter((n) => n.Status !== 'Terminating');
this.state.namespaces = this.state.namespaces.filter((n) => n.Status === 'Active');
this.state.namespaces = _.sortBy(this.state.namespaces, 'Name');
this.state.namespace = this.state.namespaces.length ? (this.state.namespaces.find((n) => n.Name === 'default') ? 'default' : this.state.namespaces[0].Name) : '';

View File

@@ -1208,7 +1208,10 @@ class KubernetesCreateApplicationController {
]);
this.nodesLimits = nodesLimits;
const nonSystemNamespaces = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
const nonSystemNamespaces = _.filter(
resourcePools,
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && resourcePool.Namespace.Status === 'Active'
);
this.allNamespaces = resourcePools.map(({ Namespace }) => Namespace.Name);
this.resourcePools = _.sortBy(nonSystemNamespaces, ({ Namespace }) => (Namespace.Name === 'default' ? 0 : 1));

View File

@@ -196,7 +196,10 @@ class KubernetesCreateConfigurationController {
try {
const resourcePools = await this.KubernetesResourcePoolService.get();
this.resourcePools = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
this.resourcePools = _.filter(
resourcePools,
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && resourcePool.Namespace.Status === 'Active'
);
this.formValues.ResourcePool = this.resourcePools[0];
await this.getConfigurations();

View File

@@ -165,7 +165,10 @@ class KubernetesConfigureController {
const allResourcePools = await this.KubernetesResourcePoolService.get();
const resourcePools = _.filter(
allResourcePools,
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && !KubernetesNamespaceHelper.isDefaultNamespace(resourcePool.Namespace.Name)
(resourcePool) =>
!KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) &&
!KubernetesNamespaceHelper.isDefaultNamespace(resourcePool.Namespace.Name) &&
resourcePool.Namespace.Status === 'Active'
);
ingressesToDel.forEach((ingress) => {

View File

@@ -284,7 +284,8 @@ class KubernetesDeployController {
async getNamespacesAsync() {
try {
const pools = await this.KubernetesResourcePoolService.get();
const namespaces = _.map(pools, 'Namespace').sort((a, b) => {
let namespaces = pools.filter((pool) => pool.Namespace.Status === 'Active');
namespaces = _.map(namespaces, 'Namespace').sort((a, b) => {
if (a.Name === 'default') {
return -1;
}

View File

@@ -50,11 +50,11 @@ class KubernetesResourcePoolsController {
} finally {
--actionCount;
if (actionCount === 0) {
await this.KubernetesNamespaceService.refreshCacheAsync();
this.$state.reload(this.$state.current);
}
}
}
await this.KubernetesNamespaceService.refreshCacheAsync();
}
removeAction(selectedItems) {
@@ -77,7 +77,7 @@ class KubernetesResourcePoolsController {
async getResourcePoolsAsync() {
try {
this.resourcePools = await this.KubernetesResourcePoolService.get('', { getQuota: true });
this.resourcePools = await this.KubernetesResourcePoolService.get('', { getQuota: true, refreshCache: true });
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retreive namespaces');
}

View File

@@ -1,16 +1,13 @@
import angular from 'angular';
import { r2a } from '@/react-tools/react2angular';
import { withControlledInput } from '@/react-tools/withControlledInput';
import { EdgeKeyDisplay } from '@/react/portainer/environments/ItemView/EdgeKeyDisplay';
import { KVMControl } from '@/react/portainer/environments/KvmView/KVMControl';
import { GpusList } from '@/react/docker/host/SetupView/GpusList';
export const environmentsModule = angular
.module('portainer.app.react.components.environments', [])
.component('edgeKeyDisplay', r2a(EdgeKeyDisplay, ['edgeKey']))
.component('kvmControl', r2a(KVMControl, ['deviceId', 'server', 'token']))
.component(
'gpusList',
r2a(withControlledInput(GpusList), ['value', 'onChange'])
'kvmControl',
r2a(KVMControl, ['deviceId', 'server', 'token'])
).name;

View File

@@ -26,7 +26,6 @@ import { Slider } from '@@/form-components/Slider';
import { TagButton } from '@@/TagButton';
import { BETeaserButton } from '@@/BETeaserButton';
import { CodeEditor } from '@@/CodeEditor';
import { InsightsBox } from '@@/InsightsBox';
import { fileUploadField } from './file-upload-field';
import { switchField } from './switch-field';
@@ -80,10 +79,6 @@ export const componentsModule = angular
.component('badge', r2a(Badge, ['type', 'className']))
.component('fileUploadField', fileUploadField)
.component('porSwitchField', switchField)
.component(
'insightsBox',
r2a(InsightsBox, ['header', 'content', 'setHtmlContent', 'insightCloseId'])
)
.component(
'passwordCheckHint',
r2a(withReactQuery(PasswordCheckHint), [

View File

@@ -211,22 +211,7 @@
<!-- !open-amt info -->
<!-- gpus info -->
<div class="mb-4">
<insights-box
ng-if="isDockerStandaloneEnv"
header="'GPU settings update'"
set-html-content="true"
insight-close-id="'gpu-settings-update-closed'"
content="'
<p>
From 2.18 on, the set-up of available GPUs for a Docker Standalone environment has been shifted from Add environment and Environment details to Host -> Setup, so as to align with other settings.
</p>
<p>
A toggle has been introduced for enabling/disabling management of GPU settings in the Portainer UI - to alleviate the performance impact of showing those settings.
</p>
<p>
The UI has been updated to clarify that GPU settings support is only for Docker Standalone (and not Docker Swarm, which was never supported in the UI).
</p>'"
></insights-box>
<gpus-insights></gpus-insights>
</div>
<!-- gpus info -->
<div class="form-group">

View File

@@ -1,7 +1,6 @@
import clsx from 'clsx';
import { Lightbulb, X } from 'lucide-react';
import { ReactNode, useMemo } from 'react';
import sanitize from 'sanitize-html';
import { ReactNode } from 'react';
import { useStore } from 'zustand';
import { Button } from '@@/buttons';
@@ -15,25 +14,11 @@ export type Props = {
insightCloseId?: string; // set if you want to be able to close the box and not show it again
};
export function InsightsBox({
header,
content,
setHtmlContent,
insightCloseId,
}: Props) {
export function InsightsBox({ header, content, insightCloseId }: Props) {
// allow to close the box and not show it again in local storage with zustand
const { addInsightIDClosed, isClosed } = useStore(insightStore);
const isInsightClosed = isClosed(insightCloseId);
// allow angular views to set html messages for the insights box
const htmlContent = useMemo(() => {
if (setHtmlContent && typeof content === 'string') {
// eslint-disable-next-line react/no-danger
return <div dangerouslySetInnerHTML={{ __html: sanitize(content) }} />;
}
return null;
}, [setHtmlContent, content]);
if (isInsightClosed) {
return null;
}
@@ -44,10 +29,16 @@ export function InsightsBox({
<Lightbulb className="h-4 text-warning-7 th-highcontrast:text-warning-6 th-dark:text-warning-6" />
</div>
<div>
<p className={clsx('mb-2 font-bold', insightCloseId && 'pr-4')}>
<p
className={clsx(
// text-[0.9em] matches .form-horizontal .control-label font-size used in many labels in portainer
'mb-2 text-[0.9em] font-medium',
insightCloseId && 'pr-4'
)}
>
{header}
</p>
<div>{htmlContent || content}</div>
<div className="small">{content}</div>
</div>
{insightCloseId && (
<Button

View File

@@ -63,7 +63,11 @@ export function Button<TasProps = unknown>({
type={type}
disabled={disabled}
className={clsx(`btn btn-${color}`, sizeClass(size), className)}
onClick={onClick}
onClick={(e) => {
if (!disabled) {
onClick?.(e);
}
}}
title={title}
// eslint-disable-next-line react/jsx-props-no-spreading
{...ariaProps}

View File

@@ -0,0 +1,29 @@
import { InsightsBox } from '@@/InsightsBox';
export function GpusInsights() {
return (
<InsightsBox
content={
<>
<p>
From 2.18 on, the set-up of available GPUs for a Docker Standalone
environment has been shifted from Add environment and Environment
details to Host -&gt; Setup, so as to align with other settings.
</p>
<p>
A toggle has been introduced for enabling/disabling management of
GPU settings in the Portainer UI - to alleviate the performance
impact of showing those settings.
</p>
<p>
The UI has been updated to clarify that GPU settings support is only
for Docker Standalone (and not Docker Swarm, which was never
supported in the UI).
</p>
</>
}
header="GPU settings update"
insightCloseId="gpu-settings-update-closed"
/>
);
}