Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 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,6 @@ 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
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.

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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ClusterVersionModel } from '../../models';
import { DetailsPage } from '../factory';
import { Conditions } from '../conditions';
import { ClusterVersionKind, K8sResourceKindReference, referenceForModel } from '../../module/k8s';
import { navFactory, ResourceSummary, SectionHeading } from '../utils';
import { navFactory, ResourceSummary, SectionHeading, UpstreamConfigDetailsItem } from '../utils';
import { breadcrumbsForGlobalConfig } from './global-config';

const clusterVersionReference: K8sResourceKindReference = referenceForModel(ClusterVersionModel);
Expand All @@ -18,7 +18,9 @@ const ClusterVersionDetails: React.FC<ClusterVersionDetailsProps> = ({ obj }) =>
<>
<div className="co-m-pane__body">
<SectionHeading text={t('public~ClusterVersion details')} />
<ResourceSummary resource={obj} />
<ResourceSummary resource={obj}>
<UpstreamConfigDetailsItem resource={obj} />
</ResourceSummary>
</div>
<div className="co-m-pane__body">
<SectionHeading text={t('public~Conditions')} id="conditions" />
Expand Down
129 changes: 129 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,129 @@
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 } 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);

const submit: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
if (customSelected) {
if (!customURL) {
setInvalidCustomURL(true);
return;
} else if (customURL === currentUpstream) {
return handlePromise(Promise.resolve(), close);
}
} 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="https://example.com/api/upgrades_info/v1/graph"
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
44 changes: 43 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 @@ -193,6 +194,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
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
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 +249,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