1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

feat(nomad): remove nomad from UI EE-6060 (#10509)

This commit is contained in:
matias-portainer 2023-10-31 15:27:20 -03:00 committed by GitHub
parent 1140804fe9
commit 8bb5129be0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
110 changed files with 39 additions and 1813 deletions

View file

@ -19,10 +19,6 @@ export enum StackType {
* Represents a stack managed via kubectl
*/
Kubernetes,
/**
* Represents a stack managed via Nomad
*/
Nomad,
}
export enum StackStatus {

View file

@ -30,8 +30,6 @@ import helm from '@/assets/ico/vendor/helm.svg?c';
import linode from '@/assets/ico/vendor/linode.svg?c';
import microsoft from '@/assets/ico/vendor/microsoft.svg?c';
import microsofticon from '@/assets/ico/vendor/microsoft-icon.svg?c';
import nomad from '@/assets/ico/vendor/nomad.svg?c';
import nomadicon from '@/assets/ico/vendor/nomad-icon.svg?c';
import openldap from '@/assets/ico/vendor/openldap.svg?c';
import proget from '@/assets/ico/vendor/proget.svg?c';
import quay from '@/assets/ico/vendor/quay.svg?c';
@ -67,8 +65,6 @@ export const SvgIcons = {
linode,
microsoft,
microsofticon,
nomad,
nomadicon,
openldap,
proget,
quay,

View file

@ -13,7 +13,6 @@ const edgePropertiesFormInitialValues: ScriptFormValues = {
envVars: '',
os: 'linux' as OS,
platform: 'k8s' as Platform,
nomadToken: '',
authEnabled: true,
tlsEnabled: false,
edgeGroupsIds: [],
@ -25,7 +24,6 @@ const edgePropertiesFormInitialValues: ScriptFormValues = {
interface Props {
edgeInfo: EdgeInfo;
commands: CommandTab[] | Partial<Record<OS, CommandTab[]>>;
isNomadTokenVisible?: boolean;
asyncMode?: boolean;
showMetaFields?: boolean;
}
@ -33,7 +31,6 @@ interface Props {
export function EdgeScriptForm({
edgeInfo,
commands,
isNomadTokenVisible,
asyncMode,
showMetaFields,
children,
@ -44,7 +41,7 @@ export function EdgeScriptForm({
<div className="form-horizontal">
<Formik
initialValues={edgePropertiesFormInitialValues}
validationSchema={() => validationSchema(isNomadTokenVisible)}
validationSchema={() => validationSchema()}
onSubmit={() => {}}
validateOnMount
>
@ -53,9 +50,6 @@ export function EdgeScriptForm({
{children}
<EdgeScriptSettingsFieldset
isNomadTokenVisible={
isNomadTokenVisible && values.platform === 'nomad'
}
hideIdGetter={edgeInfo.id !== undefined}
showMetaFields={showMetaFields}
/>

View file

@ -1,8 +1,6 @@
import { object, boolean, string } from 'yup';
import { validation as nomadTokenValidation } from './NomadTokenField';
export function validationSchema(isNomadTokenVisible?: boolean) {
export function validationSchema() {
return object().shape({
allowSelfSignedCertificates: boolean(),
envVars: string(),
@ -13,17 +11,5 @@ export function validationSchema(isNomadTokenVisible?: boolean) {
'edge id generator cannot be empty',
(value) => !!(value && value.length)
),
...nomadValidation(isNomadTokenVisible),
});
}
function nomadValidation(isNomadTokenVisible?: boolean) {
if (!isNomadTokenVisible) {
return {};
}
return {
tlsEnabled: boolean().default(false),
...nomadTokenValidation(),
};
}

View file

@ -10,17 +10,14 @@ import { TagSelector } from '@@/TagSelector';
import { EdgeGroupsSelector } from '../../edge-stacks/components/EdgeGroupsSelector';
import { NomadTokenField } from './NomadTokenField';
import { ScriptFormValues } from './types';
interface Props {
isNomadTokenVisible?: boolean;
hideIdGetter?: boolean;
showMetaFields?: boolean;
}
export function EdgeScriptSettingsFieldset({
isNomadTokenVisible,
hideIdGetter,
showMetaFields,
}: Props) {
@ -75,23 +72,6 @@ export function EdgeScriptSettingsFieldset({
</>
)}
{isNomadTokenVisible && (
<>
<NomadTokenField />
<div className="form-group">
<div className="col-sm-12">
<SwitchField
label="TLS"
labelClass="col-sm-3 col-lg-2"
checked={values.tlsEnabled}
onChange={(checked) => setFieldValue('tlsEnabled', checked)}
/>
</div>
</div>
</>
)}
<FormControl
label="Environment variables"
tooltip="Comma separated list of environment variables that will be sourced from the host where the agent is deployed."

View file

@ -1,53 +0,0 @@
import { Field, useFormikContext } from 'formik';
import { string, boolean } from 'yup';
import { FormControl } from '@@/form-components/FormControl';
import { SwitchField } from '@@/form-components/SwitchField';
import { Input } from '@@/form-components/Input';
import { ScriptFormValues } from './types';
export function NomadTokenField() {
const { values, setFieldValue, errors } =
useFormikContext<ScriptFormValues>();
return (
<>
<div className="form-group">
<div className="col-sm-12">
<SwitchField
checked={values.authEnabled}
onChange={(value) => {
if (!value) {
setFieldValue('nomadToken', '');
}
setFieldValue('authEnabled', value);
}}
label="Nomad Authentication Enabled"
tooltip="Nomad authentication is only required if you have ACL enabled"
/>
</div>
</div>
{values.authEnabled && (
<FormControl
label="Nomad Token"
inputId="nomad-token-input"
errors={errors.nomadToken}
>
<Field name="nomadToken" as={Input} id="nomad-token-input" />
</FormControl>
)}
</>
);
}
export function validation() {
return {
nomadToken: string().when('authEnabled', {
is: true,
then: string().required('Token is required'),
}),
authEnabled: boolean(),
};
}

View file

@ -35,11 +35,6 @@ export const commandsTabs: Record<string, CommandTab> = {
label: 'Docker Standalone',
command: buildLinuxStandaloneCommand,
},
nomadLinux: {
id: 'nomad',
label: 'Nomad',
command: buildLinuxNomadCommand,
},
swarmWindows: {
id: 'swarm',
label: 'Docker Swarm',
@ -237,38 +232,6 @@ export function buildLinuxKubernetesCommand(
return `${idEnvVar}curl https://downloads.portainer.io/ee${agentShortVersion}/portainer-edge-agent-setup.sh | bash -s -- "${edgeIdVar}" "${edgeKey}" "${selfSigned}" "${agentSecret}" "${allEnvVars}"`;
}
export function buildLinuxNomadCommand(
agentVersion: string,
edgeKey: string,
properties: ScriptFormValues,
useAsyncMode: boolean,
edgeId?: string,
agentSecret?: string
) {
const {
allowSelfSignedCertificates,
edgeIdGenerator,
envVars,
nomadToken = '',
tlsEnabled,
} = properties;
const agentShortVersion = getAgentShortVersion(agentVersion);
const allEnvVars = buildEnvVars(
envVars,
_.compact([useAsyncMode && 'EDGE_ASYNC=1', ...metaEnvVars(properties)])
);
const selfSigned = allowSelfSignedCertificates ? '1' : '0';
const idEnvVar = edgeIdGenerator
? `PORTAINER_EDGE_ID=$(${edgeIdGenerator}) \n\n`
: '';
const edgeIdVar = !edgeIdGenerator && edgeId ? edgeId : '$PORTAINER_EDGE_ID';
return `${idEnvVar}curl https://downloads.portainer.io/ee${agentShortVersion}/portainer-edge-agent-nomad-setup.sh | bash -s -- "${nomadToken}" "${edgeIdVar}" "${edgeKey}" "${selfSigned}" "${allEnvVars}" "${agentSecret}" "${tlsEnabled}"`;
}
function buildDockerEnvVars(envVars: string, moreVars: string[]) {
const vars = moreVars.concat(envVars.split(',').filter((s) => s.length > 0));

View file

@ -3,11 +3,10 @@ import { EnvironmentGroupId } from '@/react/portainer/environments/environment-g
import { EdgeGroup } from '../../edge-groups/types';
export type Platform = 'standalone' | 'swarm' | 'k8s' | 'nomad';
export type Platform = 'standalone' | 'swarm' | 'k8s';
export type OS = 'win' | 'linux';
export interface ScriptFormValues {
nomadToken: string;
authEnabled: boolean;
tlsEnabled: boolean;

View file

@ -24,10 +24,8 @@ import { PrivateRegistryFieldsetWrapper } from './PrivateRegistryFieldsetWrapper
import { FormValues } from './types';
import { ComposeForm } from './ComposeForm';
import { KubernetesForm } from './KubernetesForm';
import { NomadForm } from './NomadForm';
import { GitForm } from './GitForm';
import { useValidateEnvironmentTypes } from './useEdgeGroupHasType';
import { atLeastTwo } from './atLeastTwo';
interface Props {
edgeStack: EdgeStack;
@ -41,7 +39,6 @@ interface Props {
const forms = {
[DeploymentType.Compose]: ComposeForm,
[DeploymentType.Kubernetes]: KubernetesForm,
[DeploymentType.Nomad]: NomadForm,
};
export function EditEdgeStackForm({
@ -108,7 +105,6 @@ function InnerForm({
const hasKubeEndpoint = hasType(EnvironmentType.EdgeAgentOnKubernetes);
const hasDockerEndpoint = hasType(EnvironmentType.EdgeAgentOnDocker);
const hasNomadEndpoint = hasType(EnvironmentType.EdgeAgentOnNomad);
const DeploymentForm = forms[values.deploymentType];
@ -120,7 +116,7 @@ function InnerForm({
error={errors.edgeGroups}
/>
{atLeastTwo(hasKubeEndpoint, hasDockerEndpoint, hasNomadEndpoint) && (
{hasKubeEndpoint && hasDockerEndpoint && (
<TextTip>
There are no available deployment types when there is more than one
type of environment in your edge group selection (e.g. Kubernetes and
@ -142,7 +138,6 @@ function InnerForm({
value={values.deploymentType}
hasDockerEndpoint={hasType(EnvironmentType.EdgeAgentOnDocker)}
hasKubeEndpoint={hasType(EnvironmentType.EdgeAgentOnKubernetes)}
hasNomadEndpoint={hasType(EnvironmentType.EdgeAgentOnNomad)}
onChange={(value) => {
setFieldValue('content', getCachedContent(value));
setFieldValue('deploymentType', value);
@ -255,7 +250,6 @@ function useCachedContent() {
const [cachedContent, setCachedContent] = useState({
[DeploymentType.Compose]: '',
[DeploymentType.Kubernetes]: '',
[DeploymentType.Nomad]: '',
});
function handleChangeContent(type: DeploymentType, content: string) {

View file

@ -44,7 +44,6 @@ import { EnvironmentVariablesPanel } from '@@/form-components/EnvironmentVariabl
import { EnvVar } from '@@/form-components/EnvironmentVariablesFieldset/types';
import { useValidateEnvironmentTypes } from '../useEdgeGroupHasType';
import { atLeastTwo } from '../atLeastTwo';
import { PrivateRegistryFieldset } from '../../../components/PrivateRegistryFieldset';
import {
@ -201,7 +200,6 @@ function InnerForm({
const hasKubeEndpoint = hasType(EnvironmentType.EdgeAgentOnKubernetes);
const hasDockerEndpoint = hasType(EnvironmentType.EdgeAgentOnDocker);
const hasNomadEndpoint = hasType(EnvironmentType.EdgeAgentOnNomad);
return (
<Form className="form-horizontal" onSubmit={handleSubmit}>
@ -211,7 +209,7 @@ function InnerForm({
error={errors.groupIds}
/>
{atLeastTwo(hasKubeEndpoint, hasDockerEndpoint, hasNomadEndpoint) && (
{hasKubeEndpoint && hasDockerEndpoint && (
<TextTip>
There are no available deployment types when there is more than one
type of environment in your edge group selection (e.g. Kubernetes and
@ -231,7 +229,6 @@ function InnerForm({
value={values.deploymentType}
hasDockerEndpoint={hasType(EnvironmentType.EdgeAgentOnDocker)}
hasKubeEndpoint={hasType(EnvironmentType.EdgeAgentOnKubernetes)}
hasNomadEndpoint={hasType(EnvironmentType.EdgeAgentOnNomad)}
onChange={(value) => {
setFieldValue('deploymentType', value);
}}

View file

@ -1,38 +0,0 @@
import { useFormikContext } from 'formik';
import { WebEditorForm } from '@@/WebEditorForm';
import { DeploymentType } from '../../types';
import { FormValues } from './types';
export function NomadForm({
handleContentChange,
}: {
handleContentChange: (type: DeploymentType, content: string) => void;
}) {
const { errors, values } = useFormikContext<FormValues>();
return (
<WebEditorForm
value={values.content}
yaml
id="kube-manifest-editor"
placeholder="Define or paste the content of your manifest here"
onChange={(value) => handleContentChange(DeploymentType.Nomad, value)}
error={errors.content}
>
<p>
You can get more information about Nomad file format in the{' '}
<a
href="https://www.nomadproject.io/docs/job-specification"
target="_blank"
rel="noreferrer"
>
official documentation
</a>
.
</p>
</WebEditorForm>
);
}

View file

@ -1,8 +1,6 @@
import _ from 'lodash';
import { EditorType } from '@/react/edge/edge-stacks/types';
import NomadIcon from '@/assets/ico/vendor/nomad.svg?c';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { BoxSelector } from '@@/BoxSelector';
import { BoxSelectorOption } from '@@/BoxSelector/types';
@ -16,7 +14,6 @@ interface Props {
onChange(value: number): void;
hasDockerEndpoint: boolean;
hasKubeEndpoint: boolean;
hasNomadEndpoint: boolean;
allowKubeToSelectCompose?: boolean;
}
@ -25,44 +22,28 @@ export function EdgeStackDeploymentTypeSelector({
onChange,
hasDockerEndpoint,
hasKubeEndpoint,
hasNomadEndpoint,
allowKubeToSelectCompose,
}: Props) {
const deploymentOptions: BoxSelectorOption<number>[] = _.compact([
{
...compose,
value: EditorType.Compose,
disabled: () =>
allowKubeToSelectCompose
? hasNomadEndpoint
: hasNomadEndpoint || hasKubeEndpoint,
disabled: () => !allowKubeToSelectCompose && hasKubeEndpoint,
tooltip: () =>
hasNomadEndpoint || hasKubeEndpoint
? 'Cannot use this option with Edge Kubernetes or Edge Nomad environments'
hasKubeEndpoint
? 'Cannot use this option with Edge Kubernetes environments'
: '',
},
{
...kubernetes,
value: EditorType.Kubernetes,
disabled: () => hasDockerEndpoint || hasNomadEndpoint,
disabled: () => hasDockerEndpoint,
tooltip: () =>
hasDockerEndpoint || hasNomadEndpoint
? 'Cannot use this option with Edge Docker or Edge Nomad environments'
hasDockerEndpoint
? 'Cannot use this option with Edge Docker environments'
: '',
iconType: 'logo',
},
isBE && {
id: 'deployment_nomad',
icon: NomadIcon,
label: 'Nomad',
description: 'Nomad HCL format',
value: EditorType.Nomad,
disabled: () => hasDockerEndpoint || hasKubeEndpoint,
tooltip: () =>
hasDockerEndpoint || hasKubeEndpoint
? 'Cannot use this option with Edge Docker or Edge Kubernetes environments'
: '',
},
]);
return (

View file

@ -60,8 +60,6 @@ export enum DeploymentType {
Compose,
/** represent an edge stack deployed using a kubernetes manifest file */
Kubernetes,
/** represent an edge stack deployed using a nomad hcl job file */
Nomad,
}
export type EdgeStack = {
@ -100,5 +98,4 @@ export type EdgeStack = {
export enum EditorType {
Compose,
Kubernetes,
Nomad,
}

View file

@ -1,83 +0,0 @@
import { List, Settings, Boxes, Gauge } from 'lucide-react';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { DashboardItem } from '@@/DashboardItem';
import { Widget, WidgetTitle, WidgetBody } from '@@/Widget';
import { PageHeader } from '@@/PageHeader';
import { DashboardGrid } from '@@/DashboardItem/DashboardGrid';
import { Icon } from '@@/Icon';
import { useDashboard } from './useDashboard';
import { RunningStatus } from './RunningStatus';
export function DashboardView() {
const environmentId = useEnvironmentId();
const dashboardQuery = useDashboard(environmentId);
const running = dashboardQuery.data?.RunningTaskCount || 0;
const stopped = (dashboardQuery.data?.TaskCount || 0) - running;
return (
<>
<PageHeader
title="Dashboard"
breadcrumbs={[{ label: 'Environment summary' }]}
/>
{dashboardQuery.isLoading ? (
<div className="text-center" style={{ marginTop: '30%' }}>
Connecting to the Edge environment...
<Icon icon={Settings} className="!ml-1 animate-spin-slow" />
</div>
) : (
<>
<div className="row">
<div className="col-sm-12">
{/* cluster info */}
<Widget>
<WidgetTitle icon={Gauge} title="Cluster information" />
<WidgetBody className="no-padding">
<table className="table">
<tbody>
<tr>
<td>Nodes in the cluster</td>
<td>{dashboardQuery.data?.NodeCount ?? '-'}</td>
</tr>
</tbody>
</table>
</WidgetBody>
</Widget>
</div>
</div>
<div className="mx-4">
<DashboardGrid>
{/* jobs */}
<DashboardItem
value={dashboardQuery.data?.JobCount}
icon={List}
type="Nomad Job"
/>
{/* groups */}
<DashboardItem
value={dashboardQuery.data?.GroupCount}
icon={List}
type="Group"
/>
{/* tasks */}
<DashboardItem
value={dashboardQuery.data?.TaskCount}
icon={Boxes}
type="Task"
>
{/* running status of tasks */}
<RunningStatus running={running} stopped={stopped} />
</DashboardItem>
</DashboardGrid>
</div>
</>
)}
</>
);
}

View file

@ -1,31 +0,0 @@
import { Power } from 'lucide-react';
import { StatsItem } from '@@/StatsItem';
interface Props {
running: number;
stopped: number;
}
export function RunningStatus({ running, stopped }: Props) {
return (
<div>
<div>
<StatsItem
value={`${running || '-'} running`}
icon={Power}
iconClass="icon-success"
/>
{`${running || '-'} running`}
</div>
<div>
<StatsItem
value={`${stopped || '-'} stopped`}
icon={Power}
iconClass="icon-danger"
/>
{`${stopped || '-'} stopped`}
</div>
</div>
);
}

View file

@ -1 +0,0 @@
export { DashboardView } from './DashboardView';

View file

@ -1,41 +0,0 @@
import { useQuery } from 'react-query';
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
export type DashboardResponse = {
JobCount: number;
GroupCount: number;
TaskCount: number;
RunningTaskCount: number;
NodeCount: number;
};
export function useDashboard(environmentId: EnvironmentId) {
return useQuery(
['environments', environmentId, 'nomad', 'dashboard'],
() => getDashboard(environmentId),
{
meta: {
error: {
title: 'Failure',
message: 'Unable to get dashboard information',
},
},
}
);
}
export async function getDashboard(environmentId: EnvironmentId) {
try {
const { data: dashboard } = await axios.get<DashboardResponse>(
`/nomad/endpoints/${environmentId}/dashboard`,
{
params: {},
}
);
return dashboard;
} catch (e) {
throw parseAxiosError(e as Error);
}
}

View file

@ -1,35 +0,0 @@
import { History } from 'lucide-react';
import { NomadEvent } from '@/react/nomad/types';
import { Datatable } from '@@/datatables';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
import { columns } from './columns';
export interface EventsDatatableProps {
data: NomadEvent[];
isLoading: boolean;
}
const storageKey = 'nomad_events';
const settingsStore = createPersistedStore(storageKey, 'date');
export function EventsDatatable({ data, isLoading }: EventsDatatableProps) {
const tableState = useTableState(settingsStore, storageKey);
return (
<Datatable
isLoading={isLoading}
settingsManager={tableState}
columns={columns}
dataset={data}
titleIcon={History}
title="Events"
getRowId={(row) => `${row.Date}-${row.Message}-${row.Type}`}
disableSelect
/>
);
}

View file

@ -1,12 +0,0 @@
import { isoDate } from '@/portainer/filters/filters';
import { columnHelper } from './helper';
export const date = columnHelper.accessor('Date', {
header: 'Date',
id: 'date',
cell: ({ getValue }) => {
const date = getValue();
return date ? isoDate(date) : '-';
},
});

View file

@ -1,5 +0,0 @@
import { createColumnHelper } from '@tanstack/react-table';
import { NomadEvent } from '@/react/nomad/types';
export const columnHelper = createColumnHelper<NomadEvent>();

View file

@ -1,5 +0,0 @@
import { date } from './date';
import { type } from './type';
import { message } from './message';
export const columns = [date, type, message];

View file

@ -1,6 +0,0 @@
import { columnHelper } from './helper';
export const message = columnHelper.accessor('Message', {
header: 'Message',
id: 'message',
});

View file

@ -1,6 +0,0 @@
import { columnHelper } from './helper';
export const type = columnHelper.accessor('Type', {
header: 'Type',
id: 'type',
});

View file

@ -1 +0,0 @@
export { EventsDatatable } from './EventsDatatable';

View file

@ -1,41 +0,0 @@
import { useCurrentStateAndParams } from '@uirouter/react';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { PageHeader } from '@@/PageHeader';
import { EventsDatatable } from './EventsDatatable';
import { useEvents } from './useEvents';
export function EventsView() {
const environmentId = useEnvironmentId();
const { query, invalidateQuery } = useEvents();
const {
params: { jobID, taskName },
} = useCurrentStateAndParams();
const breadcrumbs = [
{
label: 'Nomad Jobs',
link: 'nomad.jobs',
linkParams: { id: environmentId },
},
{ label: jobID },
{ label: taskName },
{ label: 'Events' },
];
return (
<>
<PageHeader
title="Event list"
breadcrumbs={breadcrumbs}
reload
loading={query.isLoading || query.isFetching}
onReload={invalidateQuery}
/>
<EventsDatatable data={query.data || []} isLoading={query.isLoading} />
</>
);
}

View file

@ -1 +0,0 @@
export { EventsView } from './EventsView';

View file

@ -1,75 +0,0 @@
import { useQuery, useQueryClient } from 'react-query';
import { useCurrentStateAndParams } from '@uirouter/react';
import * as notifications from '@/portainer/services/notifications';
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { NomadEventsList } from '../../types';
export function useEvents() {
const queryClient = useQueryClient();
const {
params: {
endpointId: environmentID,
allocationID,
jobID,
taskName,
namespace,
},
} = useCurrentStateAndParams();
if (!environmentID) {
throw new Error('endpointId url param is required');
}
const key = [
'environments',
environmentID,
'nomad',
'events',
allocationID,
jobID,
taskName,
namespace,
];
function invalidateQuery() {
return queryClient.invalidateQueries(key);
}
const query = useQuery(
key,
() =>
getTaskEvents(environmentID, allocationID, jobID, taskName, namespace),
{
refetchOnWindowFocus: false,
onError: (err) => {
notifications.error('Failed loading events', err as Error, '');
},
}
);
return { query, invalidateQuery };
}
export async function getTaskEvents(
environmentId: EnvironmentId,
allocationId: string,
jobId: string,
taskName: string,
namespace: string
) {
try {
const ret = await axios.get<NomadEventsList>(
`/nomad/endpoints/${environmentId}/allocation/${allocationId}/events`,
{
params: { jobId, taskName, namespace },
}
);
return ret.data;
} catch (e) {
throw parseAxiosError(e as Error);
}
}

View file

@ -1,57 +0,0 @@
import { Clock } from 'lucide-react';
import { Job } from '@/react/nomad/types';
import { useRepeater } from '@@/datatables/useRepeater';
import { ExpandableDatatable } from '@@/datatables/ExpandableDatatable';
import { TableSettingsMenu } from '@@/datatables/TableSettingsMenu';
import { useTableState } from '@@/datatables/useTableState';
import { TasksDatatable } from './TasksDatatable';
import { columns } from './columns';
import { createStore } from './datatable-store';
import { JobsDatatableSettings } from './JobsDatatableSettings';
export interface JobsDatatableProps {
jobs: Job[];
refreshData: () => Promise<void>;
isLoading?: boolean;
}
const storageKey = 'nomad_jobs';
const settingsStore = createStore(storageKey);
export function JobsDatatable({
jobs,
refreshData,
isLoading,
}: JobsDatatableProps) {
const tableState = useTableState(settingsStore, storageKey);
useRepeater(tableState.autoRefreshRate, refreshData);
return (
<ExpandableDatatable
dataset={jobs}
columns={columns}
settingsManager={tableState}
title="Nomad Jobs"
titleIcon={Clock}
disableSelect
emptyContentLabel="No jobs found"
renderSubRow={(row) => (
<tr>
<td colSpan={Number.MAX_SAFE_INTEGER}>
<TasksDatatable data={row.original.Tasks} />
</td>
</tr>
)}
isLoading={isLoading}
renderTableSettings={() => (
<TableSettingsMenu>
<JobsDatatableSettings settings={tableState} />
</TableSettingsMenu>
)}
expandOnRowClick
/>
);
}

View file

@ -1,20 +0,0 @@
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { TableSettings } from './types';
interface Props {
settings: TableSettings;
}
export function JobsDatatableSettings({ settings }: Props) {
return (
<TableSettingsMenuAutoRefresh
value={settings.autoRefreshRate}
onChange={handleRefreshRateChange}
/>
);
function handleRefreshRateChange(autoRefreshRate: number) {
settings.setAutoRefreshRate(autoRefreshRate);
}
}

View file

@ -1,19 +0,0 @@
import { Task } from '@/react/nomad/types';
import { NestedDatatable } from '@@/datatables/NestedDatatable';
import { columns } from './columns';
export interface Props {
data: Task[];
}
export function TasksDatatable({ data }: Props) {
return (
<NestedDatatable
columns={columns}
dataset={data}
initialSortBy={{ id: 'taskName', desc: false }}
/>
);
}

View file

@ -1,46 +0,0 @@
import { Clock, FileText } from 'lucide-react';
import { CellContext } from '@tanstack/react-table';
import { Task } from '@/react/nomad/types';
import { Link } from '@@/Link';
import { Icon } from '@@/Icon';
import { columnHelper } from './helper';
export const actions = columnHelper.display({
header: 'Task Actions',
id: 'actions',
meta: {
width: '5px',
},
cell: ActionsCell,
});
export function ActionsCell({ row }: CellContext<Task, unknown>) {
const params = {
allocationID: row.original.AllocationID,
taskName: row.original.TaskName,
namespace: row.original.Namespace,
jobID: row.original.JobID,
};
return (
<div className="vertical-center text-center">
{/* events */}
<Link
to="nomad.events"
params={params}
title="Events"
className="space-right"
>
<Icon icon={Clock} className="space-right" />
</Link>
{/* logs */}
<Link to="nomad.logs" params={params} title="Logs">
<Icon icon={FileText} className="space-right" />
</Link>
</div>
);
}

View file

@ -1,10 +0,0 @@
import { columnHelper } from './helper';
export const allocationID = columnHelper.accessor('AllocationID', {
header: 'Allocation ID',
id: 'allocationID',
cell: ({ getValue }) => {
const value = getValue();
return value || '-';
},
});

View file

@ -1,5 +0,0 @@
import { createColumnHelper } from '@tanstack/react-table';
import { Task } from '@/react/nomad/types';
export const columnHelper = createColumnHelper<Task>();

View file

@ -1,15 +0,0 @@
import { taskStatus } from './taskStatus';
import { taskName } from './taskName';
import { taskGroup } from './taskGroup';
import { allocationID } from './allocationID';
import { started } from './started';
import { actions } from './actions';
export const columns = [
taskStatus,
taskName,
taskGroup,
allocationID,
actions,
started,
];

View file

@ -1,17 +0,0 @@
import moment from 'moment';
import { Task } from '@/react/nomad/types';
import { isoDate } from '@/portainer/filters/filters';
import { columnHelper } from './helper';
function accessor(row: Task) {
const momentDate = moment(row.StartedAt);
const isValid = momentDate.unix() > 0;
return isValid ? isoDate(momentDate) : '-';
}
export const started = columnHelper.accessor(accessor, {
header: 'Started',
id: 'startedName',
});

View file

@ -1,10 +0,0 @@
import { columnHelper } from './helper';
export const taskGroup = columnHelper.accessor('TaskGroup', {
header: 'Task Group',
id: 'taskGroup',
cell: ({ getValue }) => {
const value = getValue();
return value || '-';
},
});

View file

@ -1,6 +0,0 @@
import { columnHelper } from './helper';
export const taskName = columnHelper.accessor('TaskName', {
header: 'Task Name',
id: 'taskName',
});

View file

@ -1,38 +0,0 @@
import _ from 'lodash';
import clsx from 'clsx';
import { CellContext } from '@tanstack/react-table';
import { Task } from '@/react/nomad/types';
import { filterHOC } from '@@/datatables/Filter';
import { columnHelper } from './helper';
export const taskStatus = columnHelper.accessor('State', {
header: 'Task Status',
id: 'status',
meta: {
filter: filterHOC('Filter by state'),
},
cell: StateCell,
enableColumnFilter: true,
});
function StateCell({ getValue }: CellContext<Task, string>) {
const state = getValue();
const className = getClassName();
return <span className={clsx('label', className)}>{state}</span>;
function getClassName() {
if (['dead'].includes(_.toLower(state))) {
return 'label-danger';
}
if (['pending'].includes(_.toLower(state))) {
return 'label-warning';
}
return 'label-success';
}
}

View file

@ -1 +0,0 @@
export { TasksDatatable } from './TasksDatatable';

View file

@ -1,50 +0,0 @@
import { useMutation } from 'react-query';
import { Trash2 } from 'lucide-react';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { Job } from '@/react/nomad/types';
import { confirmDelete } from '@@/modals/confirm';
import { LoadingButton } from '@@/buttons/LoadingButton';
import { deleteJobs } from './delete';
interface Props {
selectedItems: Job[];
refreshData: () => Promise<void> | void;
}
export function JobActions({ selectedItems, refreshData }: Props) {
const environmentId = useEnvironmentId();
const mutation = useMutation(() => deleteJobs(environmentId, selectedItems));
return (
<LoadingButton
loadingText="Removing..."
isLoading={mutation.isLoading}
disabled={selectedItems.length < 1 || mutation.isLoading}
color="danger"
onClick={handleDeleteClicked}
icon={Trash2}
>
Remove
</LoadingButton>
);
async function handleDeleteClicked() {
const confirmed = await confirmDelete(
'Are you sure to delete all selected jobs?'
);
if (!confirmed) {
return;
}
mutation.mutate(undefined, {
onSuccess() {
return refreshData();
},
});
}
}

View file

@ -1,21 +0,0 @@
import * as notifications from '@/portainer/services/notifications';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { Job } from '@/react/nomad/types';
import { deleteJob } from '@/react/nomad/jobs/jobs.service';
export async function deleteJobs(environmentID: EnvironmentId, jobs: Job[]) {
return Promise.all(
jobs.map(async (job) => {
try {
await deleteJob(environmentID, job.ID, job.Namespace);
notifications.success('Job successfully removed', job.ID);
} catch (err) {
notifications.error(
'Failure',
err as Error,
`Failed to delete job ${job.ID}`
);
}
})
);
}

View file

@ -1,22 +0,0 @@
import { Clock } from 'lucide-react';
import { Icon } from '@@/Icon';
import { columnHelper } from './helper';
export const actions = columnHelper.display({
header: 'Job Actions',
id: 'actions',
meta: {
width: '110px',
},
cell: ActionsCell,
});
export function ActionsCell() {
return (
<div className="text-center">
<Icon icon={Clock} />
</div>
);
}

View file

@ -1,12 +0,0 @@
import { isoDate } from '@/portainer/filters/filters';
import { columnHelper } from './helper';
export const created = columnHelper.accessor('SubmitTime', {
header: 'Created',
id: 'created',
cell: ({ getValue }) => {
const date = getValue();
return date ? isoDate(parseInt(date, 10)) : '-';
},
});

View file

@ -1,5 +0,0 @@
import { createColumnHelper } from '@tanstack/react-table';
import { Job } from '@/react/nomad/types';
export const columnHelper = createColumnHelper<Job>();

View file

@ -1,13 +0,0 @@
import { Job } from '@/react/nomad/types';
import { buildExpandColumn } from '@@/datatables/expand-column';
import { name } from './name';
import { status } from './status';
import { created } from './created';
import { actions } from './actions';
import { namespace } from './namespace';
const expand = buildExpandColumn<Job>();
export const columns = [expand, name, status, namespace, actions, created];

View file

@ -1,6 +0,0 @@
import { columnHelper } from './helper';
export const name = columnHelper.accessor('ID', {
header: 'Name',
id: 'name',
});

View file

@ -1,10 +0,0 @@
import { columnHelper } from './helper';
export const namespace = columnHelper.accessor('Namespace', {
header: 'Namespace',
id: 'namespace',
cell: ({ getValue }) => {
const value = getValue();
return value || '-';
},
});

View file

@ -1,10 +0,0 @@
import { columnHelper } from './helper';
export const status = columnHelper.accessor('Status', {
header: 'Job Status',
id: 'status',
cell: ({ getValue }) => {
const value = getValue();
return value || '-';
},
});

View file

@ -1,9 +0,0 @@
import { refreshableSettings, createPersistedStore } from '@@/datatables/types';
import { TableSettings } from './types';
export function createStore(storageKey: string) {
return createPersistedStore<TableSettings>(storageKey, 'created', (set) => ({
...refreshableSettings(set),
}));
}

View file

@ -1 +0,0 @@
export { JobsDatatable } from './JobsDatatable';

View file

@ -1,13 +0,0 @@
import {
BasicTableSettings,
RefreshableTableSettings,
} from '@@/datatables/types';
export interface TableSettings
extends BasicTableSettings,
RefreshableTableSettings {}
export enum DeployType {
FDO = 'FDO',
MANUAL = 'MANUAL',
}

View file

@ -1,33 +0,0 @@
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { PageHeader } from '@@/PageHeader';
import { useJobs } from './useJobs';
import { JobsDatatable } from './JobsDatatable';
export function JobsView() {
const environmentId = useEnvironmentId();
const jobsQuery = useJobs(environmentId);
async function reloadData() {
await jobsQuery.refetch();
}
return (
<>
<PageHeader
title="Nomad Job list"
breadcrumbs={[{ label: 'Nomad Jobs' }]}
reload
loading={jobsQuery.isLoading}
onReload={reloadData}
/>
<JobsDatatable
jobs={jobsQuery.data || []}
refreshData={reloadData}
isLoading={jobsQuery.isLoading}
/>
</>
);
}

View file

@ -1 +0,0 @@
export { JobsView } from './JobsView';

View file

@ -1,34 +0,0 @@
import { useQuery } from 'react-query';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { Job } from '@/react/nomad/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
export function useJobs(environmentId: EnvironmentId) {
return useQuery<Job[]>(
['environments', environmentId, 'nomad', 'jobs'],
() => listJobs(environmentId),
{
meta: {
error: {
title: 'Failure',
message: 'Unable to list jobs',
},
},
}
);
}
export async function listJobs(environmentId: EnvironmentId) {
try {
const { data: jobs } = await axios.get<Job[]>(
`/nomad/endpoints/${environmentId}/jobs`,
{
params: {},
}
);
return jobs;
} catch (e) {
throw parseAxiosError(e as Error);
}
}

View file

@ -1,16 +0,0 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
export async function deleteJob(
environmentId: EnvironmentId,
jobId: string,
namespace: string
) {
try {
await axios.delete(`/nomad/endpoints/${environmentId}/jobs/${jobId}`, {
params: { namespace },
});
} catch (e) {
throw parseAxiosError(e as Error);
}
}

View file

@ -1,20 +0,0 @@
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
interface LeaderResponse {
Leader: string;
}
export async function getLeader(environmentId: EnvironmentId) {
try {
const { data } = await axios.get<LeaderResponse>(
`/nomad/endpoints/${environmentId}/leader`,
{
params: {},
}
);
return data;
} catch (e) {
throw parseAxiosError(e as Error);
}
}

View file

@ -1,25 +0,0 @@
export type NomadEvent = {
Type: string;
Message: string;
Date: number;
};
export type NomadEventsList = NomadEvent[];
export type Task = {
JobID: string;
Namespace: string;
TaskName: string;
State: string;
TaskGroup: string;
AllocationID: string;
StartedAt: string;
};
export type Job = {
ID: string;
Status: string;
Namespace: string;
SubmitTime: string;
Tasks: Task[];
};

View file

@ -6,7 +6,6 @@ import { getPlatformType } from '@/react/portainer/environments/utils';
import { EnvironmentStatsDocker } from './EnvironmentStatsDocker';
import { EnvironmentStatsKubernetes } from './EnvironmentStatsKubernetes';
import { EnvironmentStatsNomad } from './EnvironmentStatsNomad';
interface Props {
environment: Environment;
@ -34,10 +33,6 @@ function getComponent(platform: PlatformType, environment: Environment) {
);
case PlatformType.Docker:
return <EnvironmentStatsDocker snapshot={environment.Snapshots?.[0]} />;
case PlatformType.Nomad:
return (
<EnvironmentStatsNomad snapshot={environment.Nomad.Snapshots?.[0]} />
);
default:
return null;
}

View file

@ -1,44 +0,0 @@
import { Box, Dice4, HardDrive, List, Power } from 'lucide-react';
import { NomadSnapshot } from '@/react/portainer/environments/types';
import { addPlural } from '@/portainer/helpers/strings';
import { StatsItem } from '@@/StatsItem';
interface Props {
snapshot?: NomadSnapshot;
}
export function EnvironmentStatsNomad({ snapshot }: Props) {
if (!snapshot) {
return <>No snapshot available</>;
}
return (
<>
<StatsItem value={addPlural(snapshot.JobCount, 'job')} icon={List} />
<StatsItem value={addPlural(snapshot.GroupCount, 'group')} icon={Dice4} />
<StatsItem value={addPlural(snapshot.TaskCount, 'task')} icon={Box}>
{snapshot.TaskCount > 0 && (
<>
<StatsItem
value={snapshot.RunningTaskCount}
icon={Power}
iconClass="icon-success"
/>
<StatsItem
value={snapshot.TaskCount - snapshot.RunningTaskCount}
icon={Power}
iconClass="icon-danger"
/>
</>
)}
</StatsItem>
<StatsItem
value={addPlural(snapshot.NodeCount, 'node')}
icon={HardDrive}
/>
</>
);
}

View file

@ -268,7 +268,6 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
EnvironmentType.AgentOnKubernetes,
EnvironmentType.EdgeAgentOnKubernetes,
],
[PlatformType.Nomad]: [EnvironmentType.EdgeAgentOnNomad],
};
const typesByConnection = {

View file

@ -4,7 +4,6 @@ import { useTags } from '@/portainer/tags/queries';
import { useAgentVersionsList } from '../../environments/queries/useAgentVersionsList';
import { EnvironmentStatus, PlatformType } from '../../environments/types';
import { isBE } from '../../feature-flags/feature-flags.service';
import { useGroups } from '../../environments/environment-groups/queries';
import {
SortOptions,
@ -188,10 +187,6 @@ function getConnectionTypeOptions(platformTypes: PlatformType[]) {
ConnectionType.EdgeAgentStandard,
ConnectionType.EdgeAgentAsync,
],
[PlatformType.Nomad]: [
ConnectionType.EdgeAgentStandard,
ConnectionType.EdgeAgentAsync,
],
};
const connectionTypesDefaultOptions = [
@ -219,13 +214,6 @@ function getPlatformTypeOptions(connectionTypes: ConnectionType[]) {
{ value: PlatformType.Kubernetes, label: 'Kubernetes' },
];
if (isBE) {
platformDefaultOptions.push({
value: PlatformType.Nomad,
label: 'Nomad',
});
}
if (connectionTypes.length === 0) {
return platformDefaultOptions;
}
@ -235,11 +223,9 @@ function getPlatformTypeOptions(connectionTypes: ConnectionType[]) {
[ConnectionType.Agent]: [PlatformType.Docker, PlatformType.Kubernetes],
[ConnectionType.EdgeAgentStandard]: [
PlatformType.Kubernetes,
PlatformType.Nomad,
PlatformType.Docker,
],
[ConnectionType.EdgeAgentAsync]: [
PlatformType.Nomad,
PlatformType.Docker,
PlatformType.Kubernetes,
],

View file

@ -23,7 +23,6 @@ const commands = {
commandsTabs.k8sLinux,
commandsTabs.swarmLinux,
commandsTabs.standaloneLinux,
commandsTabs.nomadLinux,
],
win: [commandsTabs.swarmWindows, commandsTabs.standaloneWindow],
};
@ -156,7 +155,6 @@ function EdgeKeyInfo({
<EdgeScriptForm
edgeInfo={{ key: edgeKey }}
commands={commands}
isNomadTokenVisible
asyncMode={asyncMode}
showMetaFields
>

View file

@ -2,7 +2,7 @@ import { useQuery } from 'react-query';
import { withError } from '@/react-tools/react-query';
import { EnvironmentStatus } from '../types';
import { EnvironmentStatus, EnvironmentType } from '../types';
import {
EnvironmentsQueryParams,
getEnvironments,
@ -71,6 +71,24 @@ export function useEnvironmentList(
],
async () => {
const start = (page - 1) * pageLimit + 1;
// Workaround for EE-6060: filter out Nomad results when no filter is applied.
// Remove when cleaning up API.
if (!query.types || query.types.length === 0) {
const environmentTypesArray: EnvironmentType[] = [
EnvironmentType.Docker,
EnvironmentType.AgentOnDocker,
EnvironmentType.Azure,
EnvironmentType.EdgeAgentOnDocker,
EnvironmentType.KubernetesLocal,
EnvironmentType.AgentOnKubernetes,
EnvironmentType.EdgeAgentOnKubernetes,
];
// eslint-disable-next-line no-param-reassign
query.types = environmentTypesArray;
}
return getEnvironments({
start,
limit: pageLimit,

View file

@ -1,6 +1,5 @@
import { TagId } from '@/portainer/tags/types';
import { EnvironmentGroupId } from '@/react/portainer/environments/environment-groups/types';
import { Job } from '@/react/nomad/types';
import { DockerSnapshot } from '@/react/docker/snapshots/types';
export type EnvironmentId = number;
@ -20,14 +19,11 @@ export enum EnvironmentType {
AgentOnKubernetes,
// EdgeAgentOnKubernetes represents an environment(endpoint) connected to an Edge agent deployed on a Kubernetes environment(endpoint)
EdgeAgentOnKubernetes,
// EdgeAgentOnNomad represents an environment(endpoint) connected to an Edge agent deployed on a Nomad environment(endpoint)
EdgeAgentOnNomad,
}
export const EdgeTypes = [
EnvironmentType.EdgeAgentOnDocker,
EnvironmentType.EdgeAgentOnKubernetes,
EnvironmentType.EdgeAgentOnNomad,
] as const;
export enum EnvironmentStatus {
@ -75,20 +71,6 @@ export interface KubernetesSettings {
Configuration: KubernetesConfiguration;
}
export interface NomadSnapshot {
JobCount: number;
GroupCount: number;
TaskCount: number;
RunningTaskCount: number;
NodeCount: number;
Time: number;
Jobs: Job[];
}
export interface NomadSettings {
Snapshots: NomadSnapshot[];
}
export type EnvironmentEdge = {
AsyncMode: boolean;
PingInterval: number;
@ -156,7 +138,6 @@ export type Environment = {
URL: string;
Snapshots: DockerSnapshot[];
Kubernetes: KubernetesSettings;
Nomad: NomadSettings;
PublicURL?: string;
UserTrusted: boolean;
AMTDeviceGUID?: string;
@ -190,5 +171,4 @@ export enum PlatformType {
Docker,
Kubernetes,
Azure,
Nomad,
}

View file

@ -58,7 +58,7 @@ function CreateView() {
<BetaAlert
className="mb-2 ml-[15px]"
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
message="Beta feature - currently limited to standalone Linux edge devices."
/>
<div className="row">

View file

@ -82,7 +82,7 @@ function ItemView() {
<BetaAlert
className="mb-2 ml-[15px]"
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
message="Beta feature - currently limited to standalone Linux edge devices."
/>
<div className="row">

View file

@ -62,7 +62,7 @@ export function ListView() {
<BetaAlert
className="mb-2 ml-[15px]"
message="Beta feature - currently limited to standalone Linux and Nomad edge devices."
message="Beta feature - currently limited to standalone Linux edge devices."
/>
<Datatable

View file

@ -7,7 +7,6 @@ import {
import Docker from './docker.svg?c';
import Azure from './azure.svg?c';
import Kubernetes from './kubernetes.svg?c';
import Nomad from './nomad.svg?c';
const icons: {
[key in PlatformType]: SvgrComponent;
@ -15,7 +14,6 @@ const icons: {
[PlatformType.Docker]: Docker,
[PlatformType.Kubernetes]: Kubernetes,
[PlatformType.Azure]: Azure,
[PlatformType.Nomad]: Nomad,
};
export function getPlatformIcon(type: EnvironmentType) {

View file

@ -12,8 +12,6 @@ export function getPlatformType(envType: EnvironmentType) {
return PlatformType.Docker;
case EnvironmentType.Azure:
return PlatformType.Azure;
case EnvironmentType.EdgeAgentOnNomad:
return PlatformType.Nomad;
default:
throw new Error(`Environment Type ${envType} is not supported`);
}
@ -31,10 +29,6 @@ export function getPlatformTypeName(envType: EnvironmentType): string {
return PlatformType[getPlatformType(envType)];
}
export function isNomadEnvironment(envType: EnvironmentType) {
return getPlatformType(envType) === PlatformType.Nomad;
}
export function isAgentEnvironment(envType: EnvironmentType) {
return (
isEdgeEnvironment(envType) ||
@ -105,8 +99,6 @@ export function getDashboardRoute(environment: Environment) {
return 'docker.dashboard';
case PlatformType.Kubernetes:
return 'kubernetes.dashboard';
case PlatformType.Nomad:
return 'nomad.dashboard';
default:
throw new Error(`Unsupported platform ${platform}`);
}

View file

@ -2,7 +2,6 @@ import { FeatureId } from '@/react/portainer/feature-flags/enums';
import Docker from '@/assets/ico/vendor/docker.svg?c';
import Kubernetes from '@/assets/ico/vendor/kubernetes.svg?c';
import Azure from '@/assets/ico/vendor/azure.svg?c';
import Nomad from '@/assets/ico/vendor/nomad.svg?c';
import KaaS from '@/assets/ico/vendor/kaas-icon.svg?c';
import InstallK8s from '@/assets/ico/vendor/install-kubernetes.svg?c';
@ -13,7 +12,6 @@ export type EnvironmentOptionValue =
| 'dockerSwarm'
| 'kubernetes'
| 'aci'
| 'nomad'
| 'kaas'
| 'k8sInstall';
@ -56,16 +54,6 @@ export const existingEnvironmentTypes: EnvironmentOption[] = [
iconType: 'logo',
icon: Azure,
},
{
id: 'nomad',
value: 'nomad',
label: 'Nomad',
description: 'Connect to HashiCorp Nomad environment via API',
icon: Nomad,
iconType: 'logo',
feature: FeatureId.NOMAD,
disabledWhenLimited: true,
},
];
export const newEnvironmentTypes: EnvironmentOption[] = [
@ -102,7 +90,6 @@ export const formTitles: Record<EnvironmentOptionValue, string> = {
dockerSwarm: 'Connect to your Docker Swarm environment',
kubernetes: 'Connect to your Kubernetes environment',
aci: 'Connect to your ACI environment',
nomad: 'Connect to your Nomad environment',
kaas: 'Provision a KaaS environment',
k8sInstall: 'Create a Kubernetes cluster',
};

View file

@ -210,7 +210,6 @@ function useAnalyticsState() {
kaasAgent: 0,
aciApi: 0,
localEndpoint: 0,
nomadEdgeAgentStandard: 0,
dockerEdgeAgentAsync: 0,
dockerEdgeAgentStandard: 0,
});

View file

@ -14,16 +14,10 @@ import { EdgeAgentForm } from './EdgeAgentForm';
interface Props {
onCreate: (environment: Environment) => void;
commands: CommandTab[] | Partial<Record<OS, CommandTab[]>>;
isNomadTokenVisible?: boolean;
asyncMode?: boolean;
}
export function EdgeAgentTab({
onCreate,
commands,
isNomadTokenVisible,
asyncMode = false,
}: Props) {
export function EdgeAgentTab({ onCreate, commands, asyncMode = false }: Props) {
const [edgeInfo, setEdgeInfo] = useState<EdgeInfo>();
const [formKey, clearForm] = useReducer((state) => state + 1, 0);
@ -49,7 +43,6 @@ export function EdgeAgentTab({
<EdgeScriptForm
edgeInfo={edgeInfo}
commands={commands}
isNomadTokenVisible={isNomadTokenVisible}
asyncMode={asyncMode}
/>

View file

@ -9,7 +9,6 @@ export interface AnalyticsState {
kaasAgent: number;
aciApi: number;
localEndpoint: number;
nomadEdgeAgentStandard: number;
}
export type AnalyticsStateKey = keyof AnalyticsState;

View file

@ -15,7 +15,6 @@ export enum FeatureId {
K8S_CREATE_FROM_KUBECONFIG = 'k8s-create-from-kubeconfig',
K8S_EDIT_YAML = 'k8s-edit-yaml',
KAAS_PROVISIONING = 'kaas-provisioning',
NOMAD = 'nomad',
RBAC_ROLES = 'rbac-roles',
REGISTRY_MANAGEMENT = 'registry-management',
K8S_SETUP_DEFAULT = 'k8s-setup-default',

View file

@ -20,7 +20,6 @@ export async function init(edition: Edition) {
[FeatureId.K8S_CREATE_FROM_KUBECONFIG]: Edition.BE,
[FeatureId.KAAS_PROVISIONING]: Edition.BE,
[FeatureId.K8SINSTALL]: Edition.BE,
[FeatureId.NOMAD]: Edition.BE,
[FeatureId.ACTIVITY_AUDIT]: Edition.BE,
[FeatureId.EXTERNAL_AUTH_LDAP]: Edition.BE,
[FeatureId.HIDE_INTERNAL_AUTH]: Edition.BE,

View file

@ -48,7 +48,7 @@ interface Args {
isAdditionalFilesFieldVisible: boolean;
isAuthExplanationVisible: boolean;
isDockerStandalone: boolean;
deployMethod: 'compose' | 'nomad' | 'manifest';
deployMethod: 'compose' | 'manifest';
isForcePullVisible: boolean;
}

View file

@ -23,7 +23,7 @@ interface Props {
value: GitFormModel;
onChange: (value: Partial<GitFormModel>) => void;
environmentType?: 'DOCKER' | 'KUBERNETES' | undefined;
deployMethod?: 'compose' | 'nomad' | 'manifest';
deployMethod?: 'compose' | 'manifest';
isDockerStandalone?: boolean;
isAdditionalFilesFieldVisible?: boolean;
isForcePullVisible?: boolean;

View file

@ -12,8 +12,7 @@ export type ContainerPlatform =
| 'Docker Standalone'
| 'Docker Swarm'
| 'Kubernetes'
| 'Podman'
| 'Nomad';
| 'Podman';
export interface SystemInfoResponse {
platform: ContainerPlatform;

View file

@ -11,7 +11,6 @@ import {
} from '@/react/portainer/environments/types';
import { getPlatformType } from '@/react/portainer/environments/utils';
import { useEnvironment } from '@/react/portainer/environments/queries/useEnvironment';
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
import { environmentStore } from '@/react/hooks/current-environment-store';
import { Icon } from '@@/Icon';
@ -24,7 +23,6 @@ import { DockerSidebar } from './DockerSidebar';
import { KubernetesSidebar } from './KubernetesSidebar';
import { SidebarSection, SidebarSectionTitle } from './SidebarSection';
import { useSidebarState } from './useSidebarState';
import { NomadSidebar } from './NomadSidebar';
export function EnvironmentSidebar() {
const { query: currentEnvironmentQuery, clearEnvironment } =
@ -87,7 +85,6 @@ function Content({ environment, onClear }: ContentProps) {
[PlatformType.Azure]: AzureSidebar,
[PlatformType.Docker]: DockerSidebar,
[PlatformType.Kubernetes]: KubernetesSidebar,
[PlatformType.Nomad]: isBE ? NomadSidebar : null,
};
return sidebar[platform];

View file

@ -1,38 +0,0 @@
import { UserContext } from '@/react/hooks/useUser';
import { UserViewModel } from '@/portainer/models/user';
import { render, within } from '@/react-tools/test-utils';
import { TestSidebarProvider } from '../useSidebarState';
import { NomadSidebar } from './NomadSidebar';
test('dashboard items should render correctly', () => {
const { getByLabelText } = renderComponent();
const dashboardItem = getByLabelText(/Dashboard/i);
expect(dashboardItem).toBeVisible();
expect(dashboardItem).toHaveTextContent('Dashboard');
const dashboardItemElements = within(dashboardItem);
expect(
dashboardItemElements.getByRole('img', { hidden: true })
).toBeVisible();
const jobsItem = getByLabelText('Nomad Jobs');
expect(jobsItem).toBeVisible();
expect(jobsItem).toHaveTextContent('Jobs');
const jobsItemElements = within(jobsItem);
expect(jobsItemElements.getByRole('img', { hidden: true })).toBeVisible();
});
function renderComponent() {
const user = new UserViewModel({ Username: 'user' });
return render(
<UserContext.Provider value={{ user }}>
<TestSidebarProvider>
<NomadSidebar environmentId={1} />
</TestSidebarProvider>
</UserContext.Provider>
);
}

View file

@ -1,30 +0,0 @@
import { Clock } from 'lucide-react';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { DashboardLink } from '../items/DashboardLink';
import { SidebarItem } from '../SidebarItem';
interface Props {
environmentId: EnvironmentId;
}
export function NomadSidebar({ environmentId }: Props) {
return (
<>
<DashboardLink
environmentId={environmentId}
platformPath="nomad"
data-cy="nomadSidebar-dashboard"
/>
<SidebarItem
to="nomad.jobs"
params={{ endpointId: environmentId }}
icon={Clock}
label="Nomad Jobs"
data-cy="nomadSidebar-jobs"
/>
</>
);
}

View file

@ -1 +0,0 @@
export { NomadSidebar } from './NomadSidebar';