feat(kubernetes): edit yaml support EE-2855 (#8016)

pull/7609/head
Prabhat Khera 2022-11-22 09:40:44 +13:00 committed by GitHub
parent 7006c17ce4
commit 0f0513c684
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 195 additions and 0 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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>
);
}

View File

@ -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);
}

View File

@ -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>
);
}

View File

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

View File

@ -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;
}

View File

@ -21,6 +21,7 @@ type Color =
| 'link'
| 'light'
| 'dangerlight'
| 'warninglight'
| 'none';
type Size = 'xsmall' | 'small' | 'medium' | 'large';

View File

@ -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',

View File

@ -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,
};