mirror of https://github.com/portainer/portainer
feat(kubernetes): edit yaml support EE-2855 (#8016)
parent
7006c17ce4
commit
0f0513c684
|
@ -21,5 +21,7 @@
|
|||
<span id="copyNotificationYAML" style="display: none" class="small vertical-center ml-1">
|
||||
<pr-icon class="vertical-center" icon="'check'" size="'md'" mode="'success'" feather="true"></pr-icon> copied
|
||||
</span>
|
||||
|
||||
<yaml-replace class="float-right" feature-id="$ctrl.limitedFeature"></yaml-replace>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import angular from 'angular';
|
||||
import YAML from 'yaml';
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
class KubernetesYamlInspectorController {
|
||||
/* @ngInject */
|
||||
|
@ -8,6 +10,23 @@ class KubernetesYamlInspectorController {
|
|||
this.expanded = false;
|
||||
}
|
||||
|
||||
cleanYamlUnwantedFields(yml) {
|
||||
try {
|
||||
const ymls = yml.split('---');
|
||||
const cleanYmls = ymls.map((yml) => {
|
||||
const y = YAML.parse(yml);
|
||||
if (y.metadata) {
|
||||
delete y.metadata.managedFields;
|
||||
delete y.metadata.resourceVersion;
|
||||
}
|
||||
return YAML.stringify(y);
|
||||
});
|
||||
return cleanYmls.join('---\n');
|
||||
} catch (e) {
|
||||
return yml;
|
||||
}
|
||||
}
|
||||
|
||||
copyYAML() {
|
||||
this.clipboard.copyText(this.data);
|
||||
$('#copyNotificationYAML').show().fadeOut(2500);
|
||||
|
@ -19,6 +38,11 @@ class KubernetesYamlInspectorController {
|
|||
$(selector).css({ height: height });
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.data = this.cleanYamlUnwantedFields(this.data);
|
||||
this.limitedFeature = FeatureId.K8S_EDIT_YAML;
|
||||
}
|
||||
}
|
||||
|
||||
export default KubernetesYamlInspectorController;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { r2a } from '@/react-tools/react2angular';
|
|||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { YAMLReplace } from '@/kubernetes/react/views/yamlReplace';
|
||||
import { IngressesDatatableView } from '@/react/kubernetes/ingresses/IngressDatatable';
|
||||
import { CreateIngressView } from '@/react/kubernetes/ingresses/CreateIngressView';
|
||||
|
||||
|
@ -19,4 +20,10 @@ export const viewsModule = angular
|
|||
.component(
|
||||
'kubernetesIngressesCreateView',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(CreateIngressView))), [])
|
||||
)
|
||||
.component(
|
||||
'yamlReplace',
|
||||
r2a(withUIRouter(withReactQuery(withCurrentUser(YAMLReplace))), [
|
||||
'featureId',
|
||||
])
|
||||
).name;
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren';
|
||||
|
||||
interface Props {
|
||||
featureId: FeatureId;
|
||||
}
|
||||
export function YAMLReplace({ featureId }: Props) {
|
||||
return (
|
||||
<TooltipWithChildren
|
||||
className="float-right"
|
||||
heading="Apply YAML changes"
|
||||
BEFeatureID={featureId}
|
||||
message="Applies any changes that you make in the YAML editor by calling the Kubernetes API to patch the relevant resources. Any resource removals or unexpected resource additions that you make in the YAML will be ignored. Note that editing is disabled for resources in namespaces marked as system."
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
color="warninglight"
|
||||
size="small"
|
||||
onClick={() => {}}
|
||||
disabled
|
||||
>
|
||||
Apply changes
|
||||
</Button>
|
||||
</TooltipWithChildren>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
:global(portainer-tooltip) {
|
||||
@apply inline-flex;
|
||||
}
|
||||
|
||||
.tooltip-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
background-color: var(--bg-tooltip-color) !important;
|
||||
padding: 0.833em 1em !important;
|
||||
color: var(--text-tooltip-color) !important;
|
||||
border-radius: 10px !important;
|
||||
box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important;
|
||||
max-width: 400px;
|
||||
text-align: left;
|
||||
font-size: 12px !important;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.tooltip:hover {
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
.tooltip-container {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.tooltip-heading {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tooltip-beteaser {
|
||||
color: var(--ui-warning-5);
|
||||
}
|
||||
|
||||
.tooltip-beteaser:hover {
|
||||
color: var(--ui-warning-5);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import ReactTooltip from 'react-tooltip';
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { getFeatureDetails } from '@@/BEFeatureIndicator/utils';
|
||||
|
||||
import styles from './TooltipWithChildren.module.css';
|
||||
|
||||
type Position = 'top' | 'right' | 'bottom' | 'left';
|
||||
|
||||
export interface Props {
|
||||
position?: Position;
|
||||
message: string;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
heading?: string;
|
||||
BEFeatureID?: FeatureId;
|
||||
}
|
||||
|
||||
export function TooltipWithChildren({
|
||||
message,
|
||||
position = 'bottom',
|
||||
className,
|
||||
children,
|
||||
heading,
|
||||
BEFeatureID,
|
||||
}: Props) {
|
||||
const id = _.uniqueId('tooltip-');
|
||||
|
||||
const { url, limitedToBE } = BEFeatureID
|
||||
? getFeatureDetails(BEFeatureID)
|
||||
: { url: '', limitedToBE: false };
|
||||
|
||||
const messageHTML = (
|
||||
<div className={styles.tooltipContainer}>
|
||||
<div
|
||||
className={clsx(
|
||||
'w-full mb-2 inline-flex justify-between',
|
||||
styles.tooltipHeading
|
||||
)}
|
||||
>
|
||||
{heading && <span>{heading}</span>}
|
||||
{BEFeatureID && limitedToBE && (
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={styles.tooltipBeteaser}
|
||||
>
|
||||
Business Edition Only
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<div>{message}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<span
|
||||
data-html
|
||||
data-multiline
|
||||
data-tip={ReactDOMServer.renderToString(messageHTML)}
|
||||
data-for={id}
|
||||
className={clsx(styles.icon, 'inline-flex text-base')}
|
||||
>
|
||||
{children}
|
||||
<ReactTooltip
|
||||
id={id}
|
||||
multiline
|
||||
type="info"
|
||||
place={position}
|
||||
effect="solid"
|
||||
className={clsx(styles.tooltip, className)}
|
||||
arrowColor="var(--bg-tooltip-color)"
|
||||
delayHide={400}
|
||||
clickable
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { TooltipWithChildren } from './TooltipWithChildren';
|
|
@ -8,3 +8,9 @@
|
|||
.btn-none:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn-warninglight {
|
||||
@apply border-warning-5 bg-warning-2 text-black;
|
||||
@apply th-dark:bg-warning-5 th-dark:bg-opacity-10 th-dark:text-white;
|
||||
@apply th-highcontrast:bg-warning-5 th-highcontrast:bg-opacity-10 th-highcontrast:text-white;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ type Color =
|
|||
| 'link'
|
||||
| 'light'
|
||||
| 'dangerlight'
|
||||
| 'warninglight'
|
||||
| 'none';
|
||||
type Size = 'xsmall' | 'small' | 'medium' | 'large';
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ export enum FeatureId {
|
|||
K8S_RESOURCE_POOL_LB_QUOTA = 'k8s-resourcepool-Ibquota',
|
||||
K8S_RESOURCE_POOL_STORAGE_QUOTA = 'k8s-resourcepool-storagequota',
|
||||
K8S_CREATE_FROM_KUBECONFIG = 'k8s-create-from-kubeconfig',
|
||||
K8S_EDIT_YAML = 'k8s-edit-yaml',
|
||||
KAAS_PROVISIONING = 'kaas-provisioning',
|
||||
NOMAD = 'nomad',
|
||||
RBAC_ROLES = 'rbac-roles',
|
||||
|
|
|
@ -37,6 +37,7 @@ export async function init(edition: Edition) {
|
|||
[FeatureId.POD_SECURITY_POLICY_CONSTRAINT]: Edition.BE,
|
||||
[FeatureId.HIDE_DOCKER_HUB_ANONYMOUS]: Edition.BE,
|
||||
[FeatureId.CUSTOM_LOGIN_BANNER]: Edition.BE,
|
||||
[FeatureId.K8S_EDIT_YAML]: Edition.BE,
|
||||
[FeatureId.ENFORCE_DEPLOYMENT_OPTIONS]: Edition.BE,
|
||||
[FeatureId.K8S_ADM_ONLY_USR_INGRESS_DEPLY]: Edition.BE,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue