Skip to content

Commit

Permalink
feat: Support configuring HTTPS certificate for the default domain
Browse files Browse the repository at this point in the history
  • Loading branch information
CH3CHO committed Mar 2, 2024
1 parent c1ceaca commit 72bdf7c
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ public class HigressConstants {
public static final String CONTROLLER_SERVICE_HOST_DEFAULT = "localhost";
public static final int CONTROLLER_SERVICE_PORT_DEFAULT = 15014;
public static final String CONTROLLER_JWT_POLICY_DEFAULT = KubernetesConstants.JwtPolicy.THIRD_PARTY_JWT;
public static final String DEFAULT_DOMAIN = "higress-default-domain";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.higress.sdk.constant.CommonKey;
import com.alibaba.higress.sdk.constant.HigressConstants;
import com.alibaba.higress.sdk.exception.BusinessException;
import com.alibaba.higress.sdk.exception.ResourceConflictException;
import com.alibaba.higress.sdk.http.HttpStatus;
Expand Down Expand Up @@ -125,9 +127,17 @@ public Domain put(Domain domain) {
"Error occurs when replacing the ConfigMap generated by domain: " + domain.getName(), e);
}

PaginatedResult<Route> routes = routeService.list(new RoutePageQuery(domain.getName()));
if (CollectionUtils.isNotEmpty(routes.getData())) {
routes.getData().forEach(routeService::update);
List<Route> routes;
if (HigressConstants.DEFAULT_DOMAIN.equals(domain.getName())) {
PaginatedResult<Route> routeQueryResult = routeService.list(null);
routes = routeQueryResult.getData().stream().filter(r -> CollectionUtils.isEmpty(r.getDomains()))
.collect(Collectors.toList());
} else {
PaginatedResult<Route> routeQueryResult = routeService.list(new RoutePageQuery(domain.getName()));
routes = routeQueryResult.getData();
}
if (CollectionUtils.isNotEmpty(routes)) {
routes.forEach(routeService::update);
}

return kubernetesModelConverter.configMap2Domain(updatedConfigMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.bouncycastle.asn1.x509.GeneralName;

import com.alibaba.higress.sdk.constant.CommonKey;
import com.alibaba.higress.sdk.constant.HigressConstants;
import com.alibaba.higress.sdk.constant.KubernetesConstants;
import com.alibaba.higress.sdk.constant.Separators;
import com.alibaba.higress.sdk.exception.BusinessException;
Expand Down Expand Up @@ -768,7 +769,6 @@ private static void fillRouteInfo(Route route, V1ObjectMeta metadata, V1IngressS
fillHeaderAndQueryConfig(annotations, route);
fillMethodConfig(annotations, route);
fillHeaderConfigConfig(annotations, route);

}
fillRouteCors(route, metadata);
}
Expand Down Expand Up @@ -1235,16 +1235,17 @@ private void fillIngressSpec(V1Ingress ingress, Route route) {
}

private void fillIngressTls(V1ObjectMeta metadata, V1IngressSpec spec, Route route) {
if (CollectionUtils.isEmpty(route.getDomains())) {
return;
List<String> domains = route.getDomains();
if (CollectionUtils.isEmpty(domains)) {
domains = Collections.singletonList(HigressConstants.DEFAULT_DOMAIN);
}

if (route.getDomains().size() > 1) {
if (domains.size() > 1) {
throw new IllegalArgumentException("Only one domain is allowed.");
}

List<V1IngressTLS> tlses = null;
for (String domainName : route.getDomains()) {
for (String domainName : domains) {
if (Strings.isNullOrEmpty(domainName)) {
continue;
}
Expand Down Expand Up @@ -1272,7 +1273,9 @@ private void fillIngressTls(V1ObjectMeta metadata, V1IngressSpec spec, Route rou
}

V1IngressTLS tls = new V1IngressTLS();
tls.setHosts(Collections.singletonList(domain.getName()));
if (!HigressConstants.DEFAULT_DOMAIN.equals(domainName)){
tls.setHosts(Collections.singletonList(domainName));
}
tls.setSecretName(domain.getCertIdentifier());
if (tlses == null) {
tlses = new ArrayList<>();
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/interfaces/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export interface DomainResponse {
pageSize: number;
total: number;
}

export const DEFAULT_DOMAIN = "higress-default-domain";

Check warning on line 27 in frontend/src/interfaces/domain.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Newline required at end of file but not found
2 changes: 2 additions & 0 deletions frontend/src/locales/en-US/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
"certificate": "Certificate",
"action": "Action"
},
"defaultDomain": "Default Domain",
"createDomain": "Create Domain",
"EditDomain": "Edit Domain",
"deleteConfirmation": "Are you sure you want to delete <1>{{currentDomainName}}</1>?",
"domainForm": {
"name": "Domain",
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/locales/zh-CN/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
"certificate": "证书",
"action": "操作"
},
"defaultDomain": "缺省域名",
"createDomain": "创建域名",
"editDomain": "编辑域名",
"deleteConfirmation": "确定删除 <1>{{currentDomainName}}</1> 吗?",
"domainForm": {
"name": "域名",
Expand Down Expand Up @@ -494,4 +496,4 @@
"yes": "",
"no": ""
}
}
}
58 changes: 36 additions & 22 deletions frontend/src/pages/domain/components/DomainForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OptionItem } from '@/interfaces/common';
import { EnableHttpsValue, Protocol } from '@/interfaces/domain';
import { DEFAULT_DOMAIN, EnableHttpsValue, Protocol } from '@/interfaces/domain';
import { TlsCertificate } from '@/interfaces/tls-certificate';
import { getTlsCertificates } from '@/services';
import { QuestionCircleOutlined } from '@ant-design/icons';
Expand Down Expand Up @@ -56,26 +56,40 @@ const DomainForm: React.FC = forwardRef((props, ref) => {
form={form}
layout="vertical"
>
<Form.Item
label={t('domain.domainForm.name')}
required
name="name"
tooltip={t('domain.domainForm.nameTooltip')}
rules={[
{
required: true,
pattern: /^(\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,6}$/,
message: t('domain.domainForm.nameRequired'),
},
]}
>
<Input
showCount
allowClear
disabled={value}
maxLength={63}
/>
</Form.Item>
{
value?.name === DEFAULT_DOMAIN && (
<Form.Item
required
label={t('domain.domainForm.name')}
>
<Input
disabled={value}
value={t('domain.defaultDomain') as string}
/>
</Form.Item>
) || (
<Form.Item
label={t('domain.domainForm.name')}
required
name="name"
tooltip={t('domain.domainForm.nameTooltip')}
rules={[
{
required: true,
pattern: /^(\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,6}$/,
message: t('domain.domainForm.nameRequired'),
},
]}
>
<Input
showCount
allowClear
disabled={value}
maxLength={63}
/>
</Form.Item>
)
}
<Form.Item
label={t('domain.domainForm.protocol')}
required
Expand Down Expand Up @@ -142,7 +156,7 @@ const DomainForm: React.FC = forwardRef((props, ref) => {
</div>
) : null
}
</Form>
</Form >
);
});

Expand Down
43 changes: 32 additions & 11 deletions frontend/src/pages/domain/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable */
// @ts-nocheck
import { Domain, DomainResponse, EnableHttpsValue, Protocol } from '@/interfaces/domain';
import { DEFAULT_DOMAIN, Domain, DomainResponse, EnableHttpsValue, Protocol } from '@/interfaces/domain';
import { addGatewayDomain, deleteGatewayDomain, getGatewayDomains, updateGatewayDomain } from '@/services';
import { ExclamationCircleOutlined, RedoOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-layout';
Expand All @@ -27,6 +27,9 @@ const DomainList: React.FC = () => {
dataIndex: 'name',
key: 'name',
ellipsis: true,
render: (_, record) => {
return record.name === DEFAULT_DOMAIN ? t('domain.defaultDomain') : record.name;
},
},
{
title: t('domain.columns.protocol'),
Expand All @@ -45,13 +48,16 @@ const DomainList: React.FC = () => {
key: 'action',
width: 200,
align: 'center',
render: (_, record) => (
<Space size="small">
<a onClick={() => onEditConfig(record)}>{t('misc.strategy')}</a>
<a onClick={() => onEditDrawer(record)}>{t('misc.edit')}</a>
<a onClick={() => onShowModal(record)}>{t('misc.delete')}</a>
</Space>
),
render: (_, record) => {
const isDefaultDomain = record.name === DEFAULT_DOMAIN;
return (
<Space size="small">
{isDefaultDomain || (<a onClick={() => onEditConfig(record)}>{t('misc.strategy')}</a>)}
<a onClick={() => onEditDrawer(record)}>{t('misc.edit')}</a>
{isDefaultDomain || (<a onClick={() => onShowModal(record)}>{t('misc.delete')}</a>)}
</Space>
)
},
},
];

Expand All @@ -68,6 +74,21 @@ const DomainList: React.FC = () => {
manual: true,
onSuccess: (result: Domain[], params) => {
const _dataSource = result || [];

const defaultDomain: Domain = {
id: DEFAULT_DOMAIN,
name: DEFAULT_DOMAIN,
enableHttps: EnableHttpsValue.off,
};
for (let i = 0; i < _dataSource.length; ++i) {
if (_dataSource[i].name === DEFAULT_DOMAIN) {
Object.assign(defaultDomain, _dataSource[i]);
_dataSource.splice(i, 1);
break;
}
}
_dataSource.splice(0, 0, defaultDomain);

_dataSource.forEach((i) => {
i.key || (i.key = i.id || i.name);
i.mustHttps = [];
Expand Down Expand Up @@ -110,7 +131,7 @@ const DomainList: React.FC = () => {
try {
const values: DomainFormProps = formRef.current && (await formRef.current.handleSubmit());
const { name, certIdentifier } = values;
const data = { name };
const data = { name: name || currentDomain?.name };
let enableHttps = EnableHttpsValue.off;
if (values.protocol === Protocol.https) {
if (values.certIdentifier) {
Expand All @@ -119,7 +140,7 @@ const DomainList: React.FC = () => {
enableHttps = values.mustHttps?.length ? EnableHttpsValue.force : EnableHttpsValue.on;
}
Object.assign(data, { enableHttps });
if (currentDomain) {
if (currentDomain?.version) {
await updateGatewayDomain({ version: currentDomain.version, ...data } as Domain);
} else {
await addGatewayDomain(data as Domain);
Expand Down Expand Up @@ -205,7 +226,7 @@ const DomainList: React.FC = () => {
</p>
</Modal>
<Drawer
title={t('domain.createDomain')}
title={t(currentDomain ? 'domain.editDomain' : 'domain.createDomain')}
placement="right"
width={660}
onClose={handleDrawerCancel}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/route/components/RouteForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import i18n, { lngs } from '@/i18n';
import { OptionItem } from '@/interfaces/common';
import { Domain } from '@/interfaces/domain';
import { DEFAULT_DOMAIN, Domain } from '@/interfaces/domain';
import { upstreamServiceToString } from '@/interfaces/route';
import { getGatewayDomains, getGatewayServices } from '@/services';
import { InfoCircleOutlined, QuestionCircleFilled, QuestionCircleOutlined } from '@ant-design/icons';
Expand Down Expand Up @@ -56,7 +56,7 @@ const RouteForm: React.FC = forwardRef((props, ref) => {
const domains = _domains as Domain[];
domains && domains.forEach(domain => {
const { name } = domain;
_domainOptions.push({ label: name, value: name });
name !== DEFAULT_DOMAIN && _domainOptions.push({ label: name, value: name });
});
setDomainOptions(_domainOptions);

Expand Down

0 comments on commit 72bdf7c

Please sign in to comment.