Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/packages/console-shared/src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,8 @@ export enum REQUESTER_FILTER {
USER = 'user',
SYSTEM = 'system',
}

export const CLUSTER_VERSION_DEFAULT_UPSTREAM_SERVER_URL_PLACEHOLDER =
'https://api.openshift.com/api/upgrades_info/v1/graph';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spadgett for clarification, there are three scenarios (kudos to @wking):

  1. spec.upstream is unset/empty (see data/manifests/bootkube/cvo-overrides: Drop the explicit upstream installer#4112)
  2. spec.upstream is https://example.com/some/custom/thing
  3. spec.upstream is explicitly set, but happens to match the CVO's internal default

This PR will treat 1 as the default case and 2/3 as custom case. The value in this constant is used for display purposes only and will never be sent to the backend.

export const CLUSTER_VERSION_CUSTOM_UPSTREAM_SERVER_URL_PLACEHOLDER =
'https://example.com/api/upgrades_info/v1/graph';
Comment thread
florkbr marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import {
SectionHeading,
Timestamp,
truncateMiddle,
UpstreamConfigDetailsItem,
useAccessReview,
} from '../utils';
import {
Expand Down Expand Up @@ -1001,6 +1002,7 @@ export const ClusterVersionDetailsTable: React.FC<ClusterVersionDetailsTableProp
<dd>
<ResourceLink kind={referenceForModel(ClusterVersionModel)} name={cv.metadata.name} />
</dd>
<UpstreamConfigDetailsItem resource={cv} />
{autoscalers && (
<>
<dt>{t('public~Cluster autoscaler')}</dt>
Expand Down
132 changes: 132 additions & 0 deletions frontend/public/components/modals/configure-cluster-upstream-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import * as React from 'react';

import { ClusterVersionModel } from '../../models';
import { ClusterVersionKind, k8sPatch } from '../../module/k8s';
import {
ModalBody,
ModalComponentProps,
ModalSubmitFooter,
ModalTitle,
createModalLauncher,
} from '../factory/modal';
import { ExternalLink, HandlePromiseProps, withHandlePromise } from '../utils';
import { useTranslation } from 'react-i18next';
import { TFunction } from 'i18next';
import { RadioInput } from '../radio';
import {
CLUSTER_VERSION_DEFAULT_UPSTREAM_SERVER_URL_PLACEHOLDER,
CLUSTER_VERSION_CUSTOM_UPSTREAM_SERVER_URL_PLACEHOLDER,
} from '@console/shared/src/constants';
import { TextInput } from '@patternfly/react-core';
import { openshiftHelpBase } from '@console/internal/components/utils';

export const ConfigureClusterUpstreamModal = withHandlePromise(
(props: ConfigureClusterUpstreamModalProps) => {
const { cv, handlePromise, close } = props;
const currentUpstream = cv?.spec?.upstream;

const [customSelected, setCustomSelected] = React.useState(!!currentUpstream);
const [customUrl, setCustomURL] = React.useState(currentUpstream);
const [invalidCustomUrl, setInvalidCustomURL] = React.useState(false);
Comment thread
florkbr marked this conversation as resolved.
Outdated

const submit: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
if (customSelected) {
if (!customUrl) {
Comment thread
spadgett marked this conversation as resolved.
Outdated
setInvalidCustomURL(true);
return;
} else if (customUrl === currentUpstream) {
return handlePromise(Promise.resolve(), close);
Comment thread
spadgett marked this conversation as resolved.
Outdated
}
} else if (!currentUpstream) {
return handlePromise(Promise.resolve(), close);
}
const value = customSelected ? customUrl : null;
const patch = [{ op: 'add', path: '/spec/upstream', value }];
return handlePromise(k8sPatch(ClusterVersionModel, cv, patch), close);
};
const { t } = useTranslation();

return (
<form onSubmit={submit} name="form" className="modal-content modal-content--no-inner-scroll">
<ModalTitle>{t('public~Edit upstream configuration')}</ModalTitle>
<ModalBody>
<p>
{t(
'public~Select a configuration to receive updates. Updates can be configured to receive information from Red Hat or a custom update service.',
)}
</p>
<p>
<ExternalLink
href={`${openshiftHelpBase}updating/installing-update-service.html`}
text={t('public~Learn more about OpenShift local update services.')}
/>
</p>
<div className="form-group">
<fieldset>
<label>{t('public~Configuration')}</label>
<RadioInput
value="default"
onChange={() => {
setCustomSelected(false);
setInvalidCustomURL(false);
}}
checked={!customSelected}
title={t('public~Default. Receive update information from Red Hat.')}
>
<TextInput
id={'cluster-version-default-upstream-server-url'}
type="url"
isReadOnly
isDisabled
value={CLUSTER_VERSION_DEFAULT_UPSTREAM_SERVER_URL_PLACEHOLDER}
/>
</RadioInput>
<RadioInput
value="custom"
onChange={() => setCustomSelected(true)}
checked={customSelected}
title={t('public~Custom update service.')}
>
<TextInput
id={'cluster-version-custom-upstream-server-url'}
type="url"
placeholder={CLUSTER_VERSION_CUSTOM_UPSTREAM_SERVER_URL_PLACEHOLDER}
value={customUrl}
onChange={(text) => {
setCustomSelected(true);
setCustomURL(text);
setInvalidCustomURL(false);
}}
validated={invalidCustomUrl ? 'error' : 'default'}
/>
{invalidCustomUrl && (
<div className="pf-c-form">
<div className="pf-c-form__helper-text pf-m-error">
{t('public~Please enter a URL')}
</div>
</div>
)}
</RadioInput>
</fieldset>
</div>
</ModalBody>
<ModalSubmitFooter
errorMessage={props.errorMessage}
inProgress={props.inProgress}
submitText={t('public~Save')}
cancel={props.cancel}
submitDisabled={invalidCustomUrl}
/>
</form>
);
},
);

export const configureClusterUpstreamModal = createModalLauncher(ConfigureClusterUpstreamModal);

export type ConfigureClusterUpstreamModalProps = {
cv: ClusterVersionKind;
t: TFunction;
} & ModalComponentProps &
HandlePromiseProps;
5 changes: 5 additions & 0 deletions frontend/public/components/modals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ export const removeVolumeModal = (props) =>
m.removeVolumeModal(props),
);

export const configureClusterUpstreamModal = (props) =>
import(
'./configure-cluster-upstream-modal' /* webpackChunkName: "configure-cluster-upstream-modal" */
).then((m) => m.configureClusterUpstreamModal(props));

export const configureMachineAutoscalerModal = (props) =>
import(
'./configure-machine-autoscaler-modal' /* webpackChunkName: "configure-machine-autoscaler-modal" */
Expand Down
45 changes: 44 additions & 1 deletion frontend/public/components/utils/details-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
referenceFor,
Toleration,
} from '../../module/k8s';
import { labelsModal } from '../modals';
import { configureClusterUpstreamModal, labelsModal } from '../modals';
import { ClusterVersionModel } from '../../models';

const editLabelsModal = (e, props) => {
e.preventDefault();
Expand Down Expand Up @@ -157,6 +158,7 @@ export const ResourceSummary: React.FC<ResourceSummaryProps> = ({
<DetailsItem label={t('public~Owner')} obj={resource} path="metadata.ownerReferences">
<OwnerReferences resource={resource} />
</DetailsItem>
<UpstreamConfigDetailsItem resource={resource} />
</dl>
);
};
Expand Down Expand Up @@ -193,6 +195,43 @@ export const RuntimeClass: React.FC<RuntimeClassProps> = ({ obj, path }) => {
);
};

export const UpstreamConfigDetailsItem: React.SFC<UpstreamConfigDetailsItemProps> = ({
resource,
}) => {
const { t } = useTranslation();
// TODO this specific RBAC is duplicated across several modals and pages
const clusterVersionIsEditable =
useAccessReview({
group: ClusterVersionModel.apiGroup,
resource: ClusterVersionModel.plural,
verb: 'patch',
name: resource.metadata.name,
}) && window.SERVER_FLAGS.branding !== 'dedicated';
return (
<DetailsItem label={t('public~Upstream configuration')} obj={resource} path="spec.upstream">
<div>
<Button
type="button"
isInline
data-test-id="cluster-version-upstream-server-url"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
clusterVersionIsEditable && configureClusterUpstreamModal({ cv: resource });
}}
variant="link"
isDisabled={!clusterVersionIsEditable}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still render as a link or does PF render a disabled button with variant="link" as normal text?

If not, I think this is OK for now, but something we probably want to change in a follow up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spadgett it renders a disabled button with variant="link". The text is grey versus blue:
Screen Shot 2021-09-01 at 1 05 04 PM

>
{resource.spec.upstream || t('public~Default update server')}
{clusterVersionIsEditable && (
<PencilAltIcon className="co-icon-space-l pf-c-button-icon--plain" />
)}
</Button>
</div>
</DetailsItem>
);
};

export type ResourceSummaryProps = {
resource: K8sResourceKind;
showPodSelector?: boolean;
Expand All @@ -211,6 +250,10 @@ export type ResourcePodCountProps = {
resource: K8sResourceKind;
};

export type UpstreamConfigDetailsItemProps = {
resource: K8sResourceKind;
};

export type RuntimeClassProps = {
obj: K8sResourceCommon;
path?: string;
Expand Down
8 changes: 8 additions & 0 deletions frontend/public/locales/en/public.json
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,12 @@
"Additional columns": "Additional columns",
"Additional column list": "Additional column list",
"Restore default columns": "Restore default columns",
"Edit upstream configuration": "Edit upstream configuration",
"Select a configuration to receive updates. Updates can be configured to receive information from Red Hat or a custom update service.": "Select a configuration to receive updates. Updates can be configured to receive information from Red Hat or a custom update service.",
"Learn more about OpenShift local update services.": "Learn more about OpenShift local update services.",
"Default. Receive update information from Red Hat.": "Default. Receive update information from Red Hat.",
"Custom update service.": "Custom update service.",
"Please enter a URL": "Please enter a URL",
"Edit Pod count": "Edit Pod count",
"{{resourceKinds}} maintain the desired number of healthy pods.": "{{resourceKinds}} maintain the desired number of healthy pods.",
"{{resourceKinds}} create one or more pods and ensure that a specified number of them successfully terminate. When the specified number of completions is successfully reached, the job is complete.": "{{resourceKinds}} create one or more pods and ensure that a specified number of them successfully terminate. When the specified number of completions is successfully reached, the job is complete.",
Expand Down Expand Up @@ -1515,6 +1521,8 @@
"{{count}} annotation": "{{count}} annotation",
"{{count}} annotation_plural": "{{count}} annotations",
"Runtime class": "Runtime class",
"Upstream configuration": "Upstream configuration",
"Default update server": "Default update server",
"Remove bookmark {{content}}": "Remove bookmark {{content}}",
"Add bookmark {{content}}": "Add bookmark {{content}}",
"Remove favorite {{content}}": "Remove favorite {{content}}",
Expand Down