feat: Support more protocals for loadbalance

1. Support https、udp、http for listener creating in loadbalance
2. Fix protocals for pool creating and editing in loadbalance
3. Fix protocals for helth monitor editing in loadbalance
4. Change the component definition to get better extension functionality
5. Add certificate for https listener in loadbalance

Change-Id: I51c953c647b23d516461eb3a0c401d4216c1676b
This commit is contained in:
xusongfu 2022-05-09 18:16:25 +08:00
parent ae89da4922
commit b8675c197c
31 changed files with 1544 additions and 50 deletions

View File

@ -0,0 +1,45 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Base from '../client/base';
import { barbicanBase } from '../client/constants';
class BarbicanClient extends Base {
get baseUrl() {
return barbicanBase();
}
get resources() {
return [
{
name: 'secrets',
key: 'secrets',
responseKey: 'secret',
subResources: [
{
key: 'payload',
},
],
},
{
name: 'containers',
key: 'containers',
responseKey: 'container',
},
];
}
}
const barbicanClient = new BarbicanClient();
export default barbicanClient;

View File

@ -32,6 +32,7 @@ export const endpointVersionMap = {
swift: 'v1',
trove: 'v1.0',
manilav2: 'v2',
barbican: 'v1',
};
export const endpointsDefault = {
@ -69,6 +70,7 @@ export const octaviaBase = () => getOpenstackEndpoint('octavia');
export const swiftBase = () => getOpenstackEndpoint('swift');
export const troveBase = () => getOpenstackEndpoint('trove');
export const manilaBase = () => getOpenstackEndpoint('manilav2');
export const barbicanBase = () => getOpenstackEndpoint('barbican');
export const ironicOriginEndpoint = () => getOriginEndpoint('ironic');
export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');

View File

@ -25,6 +25,7 @@ import ironic from './ironic';
import swift from './swift';
import trove from './trove';
import manila from './manila';
import barbican from './barbican';
const client = {
skyline,
@ -40,6 +41,7 @@ const client = {
swift,
trove,
manila,
barbican,
};
window.client = client;

View File

@ -540,7 +540,7 @@ export default class BaseForm extends React.Component {
renderFormItems() {
try {
return this.formItems.map((it, index) => {
const { name, display = true } = it;
const { name, display = true, ...rest } = it;
if (!display) {
return '';
}
@ -552,7 +552,8 @@ export default class BaseForm extends React.Component {
id={`form-item-col-${name}`}
>
<FormItem
{...it}
{...rest}
name={name}
key={`form-item-${index}`}
formRef={this.formRef}
/>

View File

@ -43,6 +43,7 @@ export default class index extends Component {
handleUpload = async (file) => {
const value = await getText(file);
this.onChange(value);
return false;
};
onChangeInput = (value) => {

View File

@ -367,6 +367,28 @@ const renderMenu = (t) => {
},
],
},
{
path: '/network/certificate-admin',
name: t('Certificate Management'),
key: 'certificateAdmin',
level: 1,
children: [
{
path: /^\/network\/certificate-container-admin\/detail\/.[^/]+$/,
name: t('Certificate Detail'),
key: 'certificateContainerDetailAdmin',
level: 2,
routePath: '/network/certificate-container-admin/detail/:id',
},
{
path: /^\/network\/certificate-secret-admin\/detail\/.[^/]+$/,
name: t('Certificate Detail'),
key: 'certificateSecretDetailAdmin',
level: 2,
routePath: '/network/certificate-secret-admin/detail/:id',
},
],
},
{
path: '/network/vpn-admin',
name: t('VPN'),

View File

@ -346,6 +346,28 @@ const renderMenu = (t) => {
},
],
},
{
path: '/network/certificate',
name: t('Certificate Management'),
key: 'certificate',
level: 1,
children: [
{
path: /^\/network\/certificate-container\/detail\/.[^/]+$/,
name: t('Certificate Detail'),
key: 'certificateContainerDetail',
level: 2,
routePath: '/network/certificate-container/detail/:id',
},
{
path: /^\/network\/certificate-secret\/detail\/.[^/]+$/,
name: t('Certificate Detail'),
key: 'certificateSecretDetail',
level: 2,
routePath: '/network/certificate-secret/detail/:id',
},
],
},
{
path: '/network/vpn',
name: t('VPN'),

View File

@ -249,6 +249,7 @@
"Burkina Faso": "Burkina Faso",
"Burst limit": "Burst limit",
"Burundi": "Burundi",
"CA Certificate": "CA Certificate",
"CIDR": "CIDR",
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)",
"CIFS": "CIFS",
@ -282,6 +283,11 @@
"CephFS": "CephFS",
"Cephx": "Cephx",
"Cert": "Cert",
"Certificate Content": "Certificate Content",
"Certificate Detail": "Certificate Detail",
"Certificate Management": "Certificate Management",
"Certificate Name": "Certificate Name",
"Certificate Type": "Certificate Type",
"Chad": "Chad",
"Change Password": "Change Password",
"Change Type": "Change Type",
@ -394,6 +400,7 @@
"Create Backups": "Create Backups",
"Create Bandwidth Limit Rule": "Create Bandwidth Limit Rule",
"Create Bare Metal Node": "Create Bare Metal Node",
"Create Certificate": "Create Certificate",
"Create Complete": "Create Complete",
"Create Configurations": "Create Configurations",
"Create Container": "Create Container",
@ -539,6 +546,7 @@
"Delete Backup": "Delete Backup",
"Delete Bandwidth Egress Rules": "Delete Bandwidth Egress Rules",
"Delete Bandwidth Ingress Rules": "Delete Bandwidth Ingress Rules",
"Delete Certificate": "Delete Certificate",
"Delete Complete": "Delete Complete",
"Delete Configuration": "Delete Configuration",
"Delete Container": "Delete Container",
@ -975,6 +983,7 @@
"If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.",
"If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?",
"If checked, the network will be enable.": "If checked, the network will be enable.",
"If it is an SNI type certificate, a domain name needs to be specified": "If it is an SNI type certificate, a domain name needs to be specified",
"If no gateway is specified, the first IP address will be defaulted.": "If no gateway is specified, the first IP address will be defaulted.",
"If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "If not provided, the roles assigned to the application credential will be the same as the roles in the current token.",
"If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.",
@ -1407,6 +1416,7 @@
"On": "On",
"One entry per line(e.g. 114.114.114.114)": "One entry per line(e.g. 114.114.114.114)",
"One entry per line(e.g. {ip})": "One entry per line(e.g. {ip})",
"One-way authentication": "One-way authentication",
"Online": "Online",
"Online Resize": "Online Resize",
"Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field": "Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field",
@ -1438,6 +1448,7 @@
"Owned Subnet": "Owned Subnet",
"Owner": "Owner",
"Ownership of a volume can be transferred from one project to another. The transfer process of the volume needs to perform the transfer operation in the original owner's project, and complete the \"accept\" operation in the receiver's project.": "Ownership of a volume can be transferred from one project to another. The transfer process of the volume needs to perform the transfer operation in the original owner's project, and complete the \"accept\" operation in the receiver's project.",
"PEM encoding": "PEM encoding",
"PFS": "PFS",
"PG Count": "PG Count",
"PGM": "PGM",
@ -1495,6 +1506,7 @@
"Please confirm your password!": "Please confirm your password!",
"Please enter JSON in the correct format!": "Please enter JSON in the correct format!",
"Please enter URL!": "Please enter URL!",
"Please enter a correct domain starting with \"http://\" or \"https://\"!": "Please enter a correct domain starting with \"http://\" or \"https://\"!",
"Please enter a file link starting with \"http://\" or \"https://\"!": "Please enter a file link starting with \"http://\" or \"https://\"!",
"Please enter a memory page size, such as: 1024, 1024MB": "Please enter a memory page size, such as: 1024, 1024MB",
"Please enter a valid ASCII code": "Please enter a valid ASCII code",
@ -1597,6 +1609,7 @@
"Prepare Template": "Prepare Template",
"Previous": "Previous",
"Private": "Private",
"Private Key": "Private Key",
"Profile": "Profile",
"Progress": "Progress",
"Project": "Project",
@ -1769,7 +1782,10 @@
"Rwanda": "Rwanda",
"SCTP": "SCTP",
"SNAT Enabled": "SNAT Enabled",
"SNI Certificate": "SNI Certificate",
"SNI Enabled": "SNI Enabled",
"SOURCE_IP": "Source IP",
"SSL Parsing Method": "SSL Parsing Method",
"Saint Vincent and the Grenadines": "Saint Vincent and the Grenadines",
"Same subnet with LB": "Same subnet with LB",
"Samoa": "Samoa",
@ -1804,6 +1820,7 @@
"Selected list": "Selected list",
"Senegal": "Senegal",
"Serbia": "Serbia",
"Server Certificate": "Server Certificate",
"Server Group": "Server Group",
"Server Group Detail": "Server Group Detail",
"Server Group Member": "Server Group Member",
@ -2112,6 +2129,7 @@
"Turkmenistan": "Turkmenistan",
"Turks and Caicos Islands": "Turks and Caicos Islands",
"Tuvalu": "Tuvalu",
"Two-way authentication": "Two-way authentication",
"Type": "Type",
"Type of datastore": "Type of datastore",
"UDPLite": "UDPLite",
@ -2327,6 +2345,7 @@
"be soft rebooted": "be soft rebooted",
"be started": "be started",
"be stopped": "be stopped",
"certificate": "certificate",
"cidr": "CIDR",
"cinder services": "cinder services",
"compute hosts": "compute hosts",
@ -2370,6 +2389,7 @@
"delete backup": "delete backup",
"delete bandwidth egress rules": "delete bandwidth egress rules",
"delete bandwidth ingress rules": "delete bandwidth ingress rules",
"delete certificate": "delete certificate",
"delete container": "delete container",
"delete default pool": "delete default pool",
"delete domain": "delete domain",

View File

@ -249,6 +249,7 @@
"Burkina Faso": "布基纳法索",
"Burst limit": "突发限制",
"Burundi": "布隆迪",
"CA Certificate": "CA证书",
"CIDR": "网络地址",
"CIDR Format Error(e.g. 192.168.0.0/24, 2001:DB8::/48)": "CIDR格式错误192.168.0.0/24, 2001:DB8::/48",
"CIFS": "",
@ -282,6 +283,11 @@
"CephFS": "",
"Cephx": "",
"Cert": "",
"Certificate Content": "证书内容",
"Certificate Detail": "证书详情",
"Certificate Management": "证书管理",
"Certificate Name": "证书名称",
"Certificate Type": "证书类型",
"Chad": "乍得",
"Change Password": "修改密码",
"Change Type": "修改类型",
@ -394,6 +400,7 @@
"Create Backups": "创建备份",
"Create Bandwidth Limit Rule": "创建带宽限制规则",
"Create Bare Metal Node": "创建裸机节点",
"Create Certificate": "创建证书",
"Create Complete": "创建完成",
"Create Configurations": "创建配置",
"Create Container": "创建容器",
@ -539,6 +546,7 @@
"Delete Backup": "删除备份",
"Delete Bandwidth Egress Rules": "删除带宽出方向限制",
"Delete Bandwidth Ingress Rules": "删除带宽入方向限制",
"Delete Certificate": "删除证书",
"Delete Complete": "删除完成",
"Delete Configuration": "删除配置",
"Delete Container": "删除容器",
@ -656,7 +664,7 @@
"Domain Edit": "编辑域",
"Domain ID/Name": "域ID/名称",
"Domain Manager": "域管理员",
"Domain Name": "域名",
"Domain Name": "域名",
"Domains": "域",
"Dominica": "多米尼克国",
"Down": "停止",
@ -975,6 +983,7 @@
"If OS is Linux, system will reset root password, if OS is Windows, system will reset Administrator password.": "如果操作系统是Linux系统会修改root用户密码如果是Windows系统会修改Administrator用户密码。",
"If an instance is using this flavor, deleting it will cause the instance's flavor data to be missing. Are you sure to delete {name}?": "若有云主机正在使用此 flavor删除会导致云主机的 flavor 数据缺失,确定删除 {name} ",
"If checked, the network will be enable.": "如果选中,那么网络将被启用。",
"If it is an SNI type certificate, a domain name needs to be specified": "如果是 SNI 类型证书,需制定域名",
"If no gateway is specified, the first IP address will be defaulted.": "如果不指定网关IP默认是第一个地址。",
"If not provided, the roles assigned to the application credential will be the same as the roles in the current token.": "如果不选择,那么分配给应用凭证的角色将与当前用户的角色相同。",
"If nova-compute on the host is disabled, it will be forbidden to be selected as the target host.": "如果计算节点上的nova-compute被禁用将禁止其作为目标节点。",
@ -1407,6 +1416,7 @@
"On": "开",
"One entry per line(e.g. 114.114.114.114)": "每行一条(例如: 114.114.114.114)",
"One entry per line(e.g. {ip})": "每行一条(例如: {ip})",
"One-way authentication": "单向认证",
"Online": "在线",
"Online Resize": "在线修改配置",
"Only a MAC address or an OpenFlow based datapath_id of the switch are accepted in this field": "只可填写交换机的Mac地址或者交换机基于openflow的数据路径ID",
@ -1438,6 +1448,7 @@
"Owned Subnet": "所属子网",
"Owner": "所有者",
"Ownership of a volume can be transferred from one project to another. The transfer process of the volume needs to perform the transfer operation in the original owner's project, and complete the \"accept\" operation in the receiver's project.": "卷的拥有权可以从一个项目转给另外一个。卷的转让过程需要在原拥有者的项目中执行转让操作,在接收者项目中完成“接受”操作。",
"PEM encoding": "PEM编码",
"PFS": "完全向前保密",
"PG Count": "PG数量",
"PGM": "",
@ -1495,6 +1506,7 @@
"Please confirm your password!": "请确认您的密码",
"Please enter JSON in the correct format!": "请输入正确格式的JSON",
"Please enter URL!": "请输入URL",
"Please enter a correct domain starting with \"http://\" or \"https://\"!": "请输入以“http://”或“https://”开头的域名!",
"Please enter a file link starting with \"http://\" or \"https://\"!": "请输入以“http://”或“https://”开头的文件链接!",
"Please enter a memory page size, such as: 1024, 1024MB": "请输入内存页大小1024, 1024MB",
"Please enter a valid ASCII code": "请输入有效的ASCII码",
@ -1597,6 +1609,7 @@
"Prepare Template": "准备模板",
"Previous": "上一步",
"Private": "私有",
"Private Key": "私钥",
"Profile": "概要",
"Progress": "进度",
"Project": "项目",
@ -1769,7 +1782,10 @@
"Rwanda": "卢旺达",
"SCTP": "",
"SNAT Enabled": "启用SNAT",
"SNI Certificate": "SNI证书",
"SNI Enabled": "SNI开启",
"SOURCE_IP": "源IP算法",
"SSL Parsing Method": "SSL解析方式",
"Saint Vincent and the Grenadines": "圣文森特和格林纳丁斯",
"Same subnet with LB": "与LB子网相同",
"Samoa": "美属萨摩亚",
@ -1804,6 +1820,7 @@
"Selected list": "已选列表",
"Senegal": "塞内加尔",
"Serbia": "塞尔维亚共和国",
"Server Certificate": "服务器证书",
"Server Group": "云主机组",
"Server Group Detail": "云主机组详情",
"Server Group Member": "云主机组成员",
@ -2112,6 +2129,7 @@
"Turkmenistan": "土库曼",
"Turks and Caicos Islands": "土克斯及开科斯群岛",
"Tuvalu": "图瓦卢",
"Two-way authentication": "双向认证",
"Type": "类型",
"Type of datastore": "数据存储类型",
"UDPLite": "",
@ -2327,6 +2345,7 @@
"be soft rebooted": "软重启",
"be started": "启动",
"be stopped": "关闭",
"certificate": "证书",
"cidr": "CIDR",
"cinder services": "存储服务",
"compute hosts": "计算节点",
@ -2370,6 +2389,7 @@
"delete backup": "删除备份",
"delete bandwidth egress rules": "删除出方向带宽限制规则",
"delete bandwidth ingress rules": "删除入方向带宽限制规则",
"delete certificate": "删除证书",
"delete container": "删除容器",
"delete default pool": "删除资源池",
"delete domain": "删除域",

View File

@ -0,0 +1,134 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { observer, inject } from 'mobx-react';
import Base from 'containers/List';
import globalContainersStore, {
ContainersStore,
} from 'stores/barbican/containers';
import { checkPolicyRule } from 'resources/skyline/policy';
import globalSecretsStore, { SecretsStore } from 'stores/barbican/secrets';
import { certificateMode, certificateStatus } from 'resources/octavia/lb';
import { getOptions } from 'utils/index';
import { parse } from 'qs';
import actionConfigs from './actions';
export class Certificate extends Base {
init() {
if (this.currentMode === 'SERVER') {
this.store = globalContainersStore;
this.downloadStore = new ContainersStore();
} else {
this.store = globalSecretsStore;
this.downloadStore = new SecretsStore();
}
}
get policy() {
return ['containers:get', 'secrets:get'];
}
get showDetail() {
return checkPolicyRule('secret:decrypt');
}
get name() {
return t('certificate');
}
get actionConfigs() {
if (this.isAdminPage) {
return this.currentMode === 'SERVER'
? actionConfigs.actionConfigsContainerAdmin
: actionConfigs.actionConfigsSecretAdmin;
}
return this.currentMode === 'SERVER'
? actionConfigs.actionConfigsContainer
: actionConfigs.actionConfigsSecret;
}
get currentMode() {
const params = parse(this.props.location.search.slice(1));
const { tab = 'SERVER' } = params;
return tab;
}
get routeLinkPath() {
return this.currentMode === 'SERVER'
? 'certificateContainerDetail'
: 'certificateSecretDetail';
}
updateFetchParams = (params) => {
return {
...params,
mode: this.currentMode,
};
};
getColumns = () => {
const columns = [
{
title: this.showDetail ? t('ID/Name') : t('Name'),
dataIndex: 'name',
routeName: this.showDetail
? this.getRouteName(this.routeLinkPath)
: null,
},
{
title: t('Certificate Type'),
dataIndex: 'mode',
render: (value) => certificateMode[value] || value,
},
{
title: t('Expires At'),
dataIndex: 'expiration',
valueRender: 'toLocalTime',
},
{
title: t('Domain Name'),
dataIndex: 'algorithm',
render: (value) => value || '-',
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => certificateStatus[value] || value,
},
{
title: t('Created At'),
dataIndex: 'created',
valueRender: 'toLocalTime',
},
];
return columns;
};
get searchFilters() {
const ret = [
{
label: t('Name'),
name: 'name',
},
{
label: t('Certificate Type'),
name: 'mode',
options: getOptions(certificateMode),
},
];
return ret;
}
}
export default inject('rootStore')(observer(Certificate));

View File

@ -0,0 +1,59 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { inject, observer } from 'mobx-react';
import Base from 'containers/BaseDetail';
export class BaseDetail extends Base {
get leftCards() {
const cards = [this.contentCard, this.keyPairCard];
return cards;
}
get contentCard() {
const { secret_refs = [] } = this.props.detail;
const { secret_info = {} } =
secret_refs.find((it) => it.name === 'certificate') || {};
const options = [
{
content: secret_info.payload,
copyable: true,
},
];
return {
title: t('Certificate Content'),
labelCol: 0,
options,
};
}
get keyPairCard() {
const { secret_refs = [] } = this.props.detail;
const { secret_info = {} } =
secret_refs.find((it) => it.name === 'private_key') || {};
const options = [
{
content: secret_info.payload,
copyable: true,
},
];
return {
title: t('Private Key'),
labelCol: 0,
options,
};
}
}
export default inject('rootStore')(observer(BaseDetail));

View File

@ -0,0 +1,61 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { inject, observer } from 'mobx-react';
import Base from 'containers/TabDetail';
import { ContainersStore } from 'stores/barbican/containers';
import { certificateColumns } from 'resources/octavia/lb';
import BaseDetail from './BaseDetail';
import actionConfigs from '../../actions';
export class Detail extends Base {
init() {
this.store = new ContainersStore();
}
get policy() {
return 'container:get';
}
get name() {
return 'Certificate Detail';
}
get listUrl() {
return this.getRoutePath('certificate', null, { tab: 'SERVER' });
}
get actionConfigs() {
if (this.isAdminPage) {
return actionConfigs.actionConfigsContainerAdmin;
}
return actionConfigs.actionConfigsContainer;
}
get detailInfos() {
return certificateColumns;
}
get tabs() {
return [
{
title: t('Detail Info'),
key: 'detail_info',
component: BaseDetail,
},
];
}
}
export default inject('rootStore')(observer(Detail));

View File

@ -0,0 +1,40 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { inject, observer } from 'mobx-react';
import Base from 'containers/BaseDetail';
export class BaseDetail extends Base {
get leftCards() {
const cards = [this.contentCard];
return cards;
}
get contentCard() {
const { payload } = this.props.detail;
const options = [
{
content: payload,
copyable: true,
},
];
return {
title: t('Certificate Content'),
labelCol: 0,
options,
};
}
}
export default inject('rootStore')(observer(BaseDetail));

View File

@ -0,0 +1,61 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { inject, observer } from 'mobx-react';
import Base from 'containers/TabDetail';
import { SecretsStore } from 'stores/barbican/secrets';
import { certificateColumns } from 'resources/octavia/lb';
import BaseDetail from './BaseDetail';
import actionConfigs from '../../actions';
export class Detail extends Base {
init() {
this.store = new SecretsStore();
}
get policy() {
return 'secret:get';
}
get name() {
return 'Certificate Detail';
}
get listUrl() {
return this.getRoutePath('certificate', null, { tab: 'CA' });
}
get actionConfigs() {
if (this.isAdminPage) {
return actionConfigs.actionConfigsSecretAdmin;
}
return actionConfigs.actionConfigsSecret;
}
get detailInfos() {
return certificateColumns;
}
get tabs() {
return [
{
title: t('Detail Info'),
key: 'detail_info',
component: BaseDetail,
},
];
}
}
export default inject('rootStore')(observer(Detail));

View File

@ -0,0 +1,122 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action';
import { getOptions } from 'utils/index';
import { certificateMode } from 'resources/octavia/lb';
import globalContainersStore from 'stores/barbican/containers';
import moment from 'moment';
export class CreateAction extends ModalAction {
static id = 'create-certificate';
static title = t('Create Certificate');
static policy = ['secrets:post', 'containers:post'];
init() {
this.store = globalContainersStore;
}
get name() {
return t('Create Certificate');
}
get defaultValue() {
const data = {
mode: 'SERVER',
};
return data;
}
get certificateModeOptions() {
return getOptions(certificateMode);
}
validateDomain = (rule, value) => {
if (value === undefined || value === '') return Promise.resolve();
const urlReg = /^https?:\/\/(.*)/;
if (!urlReg.test(value)) {
return Promise.reject(
t(
'Please enter a correct domain starting with "http://" or "https://"!'
)
);
}
return Promise.resolve();
};
get formItems() {
const { mode } = this.state;
return [
{
name: 'name',
label: t('Certificate Name'),
type: 'input-name',
required: true,
withoutChinese: true,
},
{
name: 'mode',
label: t('Certificate Type'),
type: 'radio',
options: this.certificateModeOptions,
},
{
name: 'certificate',
label: t('Certificate Content'),
type: 'textarea-from-file',
placeholder: t('PEM encoding'),
accept: '.crt,.pem',
required: true,
},
{
name: 'private_key',
label: t('Private Key'),
type: 'textarea-from-file',
placeholder: t('PEM encoding'),
accept: '.key,.pem',
required: true,
display: mode === 'SERVER',
},
{
name: 'domain',
label: t('Domain Name'),
type: 'input',
placeholder: t('Please input'),
hidden: mode === 'CA',
validator: this.validateDomain,
extra: t(
'If it is an SNI type certificate, a domain name needs to be specified'
),
},
{
name: 'expiration',
label: t('Expires At'),
type: 'date-picker',
showToday: false,
disabledDate: (current) => current && current <= moment().endOf('d'),
},
];
}
static allowed = () => Promise.resolve(true);
onSubmit = (values) => {
return this.store.create(values);
};
}
export default inject('rootStore')(observer(CreateAction));

View File

@ -0,0 +1,46 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ConfirmAction } from 'containers/Action';
import globalContainersStore from 'stores/barbican/containers';
export default class DeleteAction extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Certificate');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('delete certificate');
}
policy = ['secret:delete', 'container:delete'];
allowedCheckFunc = () => true;
onSubmit = (data) => {
return globalContainersStore.delete(data);
};
}

View File

@ -0,0 +1,46 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { ConfirmAction } from 'containers/Action';
import globalSecretsStore from 'stores/barbican/secrets';
export default class DeleteAction extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Certificate');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
get actionName() {
return t('delete certificate');
}
policy = 'secret:delete';
allowedCheckFunc = () => true;
onSubmit = (data) => {
return globalSecretsStore.delete(data);
};
}

View File

@ -0,0 +1,60 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import CreateAction from './Create';
import DeleteContainerAction from './DeleteContainer';
import DeleteSecretAction from './DeleteSecret';
const actionConfigsContainer = {
rowActions: {
firstAction: DeleteContainerAction,
moreActions: [],
},
batchActions: [DeleteContainerAction],
primaryActions: [CreateAction],
};
const actionConfigsContainerAdmin = {
rowActions: {
firstAction: DeleteContainerAction,
moreActions: [],
},
batchActions: [DeleteContainerAction],
primaryActions: [],
};
const actionConfigsSecret = {
rowActions: {
firstAction: DeleteSecretAction,
moreActions: [],
},
batchActions: [DeleteSecretAction],
primaryActions: [CreateAction],
};
const actionConfigsSecretAdmin = {
rowActions: {
firstAction: DeleteSecretAction,
moreActions: [],
},
batchActions: [DeleteSecretAction],
primaryActions: [],
};
export default {
actionConfigsContainer,
actionConfigsContainerAdmin,
actionConfigsSecret,
actionConfigsSecretAdmin,
};

View File

@ -0,0 +1,37 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { observer, inject } from 'mobx-react';
import Base from 'containers/TabList';
import CertificateTab from './Certificate';
export class Certificate extends Base {
get tabs() {
const tabs = [
{
title: t('Server Certificate'),
key: 'SERVER',
component: CertificateTab,
},
{
title: t('CA Certificate'),
key: 'CA',
component: CertificateTab,
},
];
return tabs;
}
}
export default inject('rootStore')(observer(Certificate));

View File

@ -15,6 +15,13 @@
import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action';
import { ListenerStore } from 'stores/octavia/listener';
import { ContainersStore } from 'stores/barbican/containers';
import { SecretsStore } from 'stores/barbican/secrets';
import {
certificateColumns,
listenerProtocols,
sslParseMethod,
} from 'resources/octavia/lb';
export class Create extends ModalAction {
static id = 'create_listener';
@ -29,29 +36,62 @@ export class Create extends ModalAction {
static policy = 'os_load-balancer_api:listener:post';
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
static allowed = (item) =>
Promise.resolve(item.provisioning_status === 'ACTIVE');
init() {
this.store = new ListenerStore();
this.containersStore = new ContainersStore();
this.secretsStore = new SecretsStore();
this.fetchContainers();
this.fetchSecrets();
}
fetchContainers() {
this.containersStore.fetchList();
}
fetchSecrets() {
this.secretsStore.fetchList({ mode: 'CA' });
}
get ServerCertificate() {
return this.containersStore.list.data || [];
}
get CaCertificate() {
return this.secretsStore.list.data || [];
}
get SNICertificate() {
return (this.containersStore.list.data || []).filter(
(it) => !!it.algorithm
);
}
get nameForStateUpdate() {
return ['protocol', 'ssl_parsing_method', 'sni_enabled'];
}
get defaultValue() {
return {
protocol: 'TCP',
ssl_parsing_method: 'one-way',
sni_enabled: false,
connection_limit: -1,
};
}
onSubmit = (values) => {
const data = {
...values,
loadbalancer_id: this.containerProps.detail.id,
};
return this.store.create(data);
};
get formItems() {
const { protocol, ssl_parsing_method, sni_enabled } = this.state;
return [
{
name: 'name',
@ -68,13 +108,74 @@ export class Create extends ModalAction {
name: 'protocol',
label: t('Protocol'),
type: 'select',
options: [
options: listenerProtocols,
required: true,
},
{
name: 'ssl_parsing_method',
label: t('SSL Parsing Method'),
type: 'select',
options: sslParseMethod,
required: true,
display: protocol === 'TERMINATED_HTTPS',
},
{
name: 'default_tls_container_ref',
label: t('Server Certificate'),
type: 'select-table',
required: true,
data: this.ServerCertificate,
isLoading: this.containersStore.list.isLoading,
isMulti: false,
filterParams: [
{
label: 'TCP',
value: 'TCP',
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display: protocol === 'TERMINATED_HTTPS',
},
{
name: 'client_ca_tls_container_ref',
label: t('CA Certificate'),
type: 'select-table',
required: true,
data: this.CaCertificate,
isLoading: this.secretsStore.list.isLoading,
isMulti: false,
filterParams: [
{
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display:
protocol === 'TERMINATED_HTTPS' && ssl_parsing_method === 'two-way',
},
{
name: 'sni_enabled',
label: t('SNI Enabled'),
type: 'switch',
display: protocol === 'TERMINATED_HTTPS',
},
{
name: 'sni_container_refs',
label: t('SNI Certificate'),
type: 'select-table',
required: true,
data: this.SNICertificate,
isLoading: this.containersStore.list.isLoading,
isMulti: false,
filterParams: [
{
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display: protocol === 'TERMINATED_HTTPS' && sni_enabled,
},
{
name: 'protocol_port',
@ -92,6 +193,35 @@ export class Create extends ModalAction {
},
];
}
onSubmit = (values) => {
const {
sni_enabled,
ssl_parsing_method,
default_tls_container_ref,
client_ca_tls_container_ref,
sni_container_refs,
...rest
} = values;
const data = {
...rest,
loadbalancer_id: this.containerProps.detail.id,
};
if (default_tls_container_ref) {
data.default_tls_container_ref =
default_tls_container_ref.selectedRows[0].container_ref;
}
if (client_ca_tls_container_ref) {
data.client_ca_tls_container_ref =
client_ca_tls_container_ref.selectedRows[0].secret_ref;
}
if (sni_container_refs) {
data.sni_container_refs = [
sni_container_refs.selectedRows[0].container_ref,
];
}
return this.store.create(data);
};
}
export default inject('rootStore')(observer(Create));

View File

@ -17,6 +17,7 @@ import { ModalAction } from 'containers/Action';
import globalPoolStore from 'stores/octavia/pool';
import globalLbaasStore from 'stores/octavia/loadbalancer';
import { Algorithm, algorithmTip } from 'resources/octavia/pool';
import { poolProtocols } from 'resources/octavia/lb';
export class CreatePool extends ModalAction {
static id = 'pool-create';
@ -50,10 +51,9 @@ export class CreatePool extends ModalAction {
);
};
get defaultValue() {
return {
pool_protocol: 'TCP',
};
get filterOptions() {
const { protocol = '' } = this.item;
return poolProtocols.filter((it) => protocol.includes(it.label));
}
init() {
@ -95,12 +95,7 @@ export class CreatePool extends ModalAction {
name: 'protocol',
label: t('Pool Protocol'),
type: 'select',
options: [
{
label: 'TCP',
value: 'TCP',
},
],
options: this.filterOptions,
required: true,
},
];

View File

@ -17,7 +17,7 @@ import { ModalAction } from 'containers/Action';
import globalHealthMonitorStore, {
HealthMonitorStore,
} from 'stores/octavia/health-monitor';
import { BackendProtocol } from 'resources/octavia/pool';
import { healthProtocols } from 'resources/octavia/lb';
import { PoolStore } from 'stores/octavia/pool';
import globalLbaasStore from 'stores/octavia/loadbalancer';
@ -52,6 +52,11 @@ export class EditHealthMonitor extends ModalAction {
};
}
get filteredProtocolOptions() {
const { protocol = '' } = this.item;
return healthProtocols.filter((it) => protocol.includes(it.label));
}
get defaultValue() {
const { operating_status, type, delay, timeout, max_retries } =
this.store.detail;
@ -141,7 +146,7 @@ export class EditHealthMonitor extends ModalAction {
name: 'type',
label: t('HealthMonitor Type'),
type: 'select',
options: BackendProtocol,
options: this.filteredProtocolOptions,
hidden: !admin_state_up && healthmonitor_id,
required: true,
disabled: !!healthmonitor_id,

View File

@ -15,14 +15,13 @@
import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action';
import globalPoolStore from 'stores/octavia/pool';
import { BackendProtocol, Algorithm } from 'resources/octavia/pool';
import { Algorithm } from 'resources/octavia/pool';
import { poolProtocols } from 'resources/octavia/lb';
import globalLbaasStore from 'stores/octavia/loadbalancer';
export class EditPoolInfo extends ModalAction {
init() {
this.state = {
pool: {},
};
this.state.pool = {};
this.store = globalPoolStore;
this.getPoolDetail();
}
@ -44,6 +43,11 @@ export class EditPoolInfo extends ModalAction {
};
}
get filteredProtocolOptions() {
const { pool: { protocol = '' } = {} } = this.state;
return poolProtocols.filter((it) => protocol.includes(it.label));
}
get defaultValue() {
const { pool } = this.state;
const { name, description, protocol, lb_algorithm } = pool;
@ -101,7 +105,7 @@ export class EditPoolInfo extends ModalAction {
name: 'protocol',
label: t('Protocol'),
type: 'select',
options: BackendProtocol,
options: this.filteredProtocolOptions,
required: true,
},
{

View File

@ -83,6 +83,12 @@ export class StepCreate extends StepAction {
vip_address,
vip_network_id,
enableHealthMonitor,
listener_protocol,
listener_ssl_parsing_method,
listener_sni_enabled,
listener_default_tls_container_ref,
listener_client_ca_tls_container_ref,
listener_sni_container_refs,
...rest
} = values;
const data = {
@ -96,7 +102,29 @@ export class StepCreate extends StepAction {
data.vip_address = ip_address.ip;
}
const listenerData = {};
const listenerData = {
protocol: listener_protocol,
};
if (listener_protocol === 'TERMINATED_HTTPS') {
if (listener_default_tls_container_ref) {
listenerData.default_tls_container_ref =
listener_default_tls_container_ref.selectedRows[0].container_ref;
}
if (
listener_ssl_parsing_method === 'two-way' &&
listener_client_ca_tls_container_ref
) {
listenerData.client_ca_tls_container_ref =
listener_client_ca_tls_container_ref.selectedRows[0].secret_ref;
}
if (listener_sni_enabled && listener_sni_container_refs) {
listenerData.sni_container_refs = [
listener_sni_container_refs.selectedRows[0].container_ref,
];
}
}
const poolData = {};
const healthMonitorData = {};
Object.keys(rest).forEach((i) => {

View File

@ -14,6 +14,7 @@
import { inject, observer } from 'mobx-react';
import Base from 'components/Form';
import { healthProtocols } from 'resources/octavia/lb';
export class HealthMonitorStep extends Base {
get title() {
@ -28,8 +29,9 @@ export class HealthMonitorStep extends Base {
return true;
}
get nameForStateUpdate() {
return ['enableHealthMonitor'];
get filteredProtocolOptions() {
const { context: { listener_protocol = '' } = {} } = this.props;
return healthProtocols.filter((it) => listener_protocol.includes(it.label));
}
get defaultValue() {
@ -38,6 +40,7 @@ export class HealthMonitorStep extends Base {
health_delay: 5,
health_timeout: 3,
health_max_retries: 3,
health_type: '',
};
}
@ -109,7 +112,7 @@ export class HealthMonitorStep extends Base {
name: 'health_type',
label: t('Health Monitor Type'),
type: 'select',
options: ['PING', 'TCP'].map((i) => ({ label: i, value: i })),
options: this.filteredProtocolOptions,
required: true,
hidden: !enableHealthMonitor,
},

View File

@ -14,8 +14,22 @@
import { inject, observer } from 'mobx-react';
import Base from 'components/Form';
import {
certificateColumns,
listenerProtocols,
sslParseMethod,
} from 'resources/octavia/lb';
import { ContainersStore } from 'stores/barbican/containers';
import { SecretsStore } from 'stores/barbican/secrets';
export class ListenerStep extends Base {
init() {
this.containersStore = new ContainersStore();
this.secretsStore = new SecretsStore();
this.fetchContainers();
this.fetchSecrets();
}
get title() {
return 'Listener Detail';
}
@ -28,16 +42,52 @@ export class ListenerStep extends Base {
return true;
}
fetchContainers() {
this.containersStore.fetchList();
}
fetchSecrets() {
this.secretsStore.fetchList({ mode: 'CA' });
}
get SERVERSecrets() {
return this.containersStore.list.data || [];
}
get CASecrets() {
return this.secretsStore.list.data || [];
}
get SNISecrets() {
return (this.containersStore.list.data || []).filter(
(it) => !!it.algorithm
);
}
get defaultValue() {
return {
pool_protocol: 'TCP',
listener_ssl_parsing_method: 'one-way',
listener_sni_enabled: false,
listener_connection_limit: -1,
};
}
get nameForStateUpdate() {
return [
'listener_protocol',
'listener_ssl_parsing_method',
'listener_sni_enabled',
];
}
allowed = () => Promise.resolve();
get formItems() {
const {
listener_protocol,
listener_ssl_parsing_method,
listener_sni_enabled,
} = this.state;
return [
{
name: 'listener_name',
@ -54,13 +104,82 @@ export class ListenerStep extends Base {
name: 'listener_protocol',
label: t('Listener Protocol'),
type: 'select',
options: [
options: listenerProtocols,
onChange: () => {
this.updateContext({
pool_protocol: '',
health_type: '',
});
},
required: true,
},
{
name: 'listener_ssl_parsing_method',
label: t('SSL Parsing Method'),
type: 'select',
options: sslParseMethod,
required: true,
display: listener_protocol === 'TERMINATED_HTTPS',
},
{
name: 'listener_default_tls_container_ref',
label: t('Server Certificate'),
type: 'select-table',
required: true,
data: this.SERVERSecrets,
isLoading: false,
isMulti: false,
filterParams: [
{
label: 'TCP',
value: 'TCP',
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display: listener_protocol === 'TERMINATED_HTTPS',
},
{
name: 'listener_client_ca_tls_container_ref',
label: t('CA Certificate'),
type: 'select-table',
required: true,
data: this.CASecrets,
isLoading: false,
isMulti: false,
filterParams: [
{
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display:
listener_protocol === 'TERMINATED_HTTPS' &&
listener_ssl_parsing_method === 'two-way',
},
{
name: 'listener_sni_enabled',
label: t('SNI Enabled'),
type: 'switch',
display: listener_protocol === 'TERMINATED_HTTPS',
},
{
name: 'listener_sni_container_refs',
label: t('SNI Certificate'),
type: 'select-table',
required: true,
data: this.SNISecrets,
isLoading: false,
isMulti: false,
filterParams: [
{
label: t('Name'),
name: 'name',
},
],
columns: certificateColumns,
display:
listener_protocol === 'TERMINATED_HTTPS' && listener_sni_enabled,
},
{
name: 'listener_protocol_port',

View File

@ -15,6 +15,7 @@
import { inject, observer } from 'mobx-react';
import Base from 'components/Form';
import { Algorithm, algorithmTip } from 'resources/octavia/pool';
import { poolProtocols } from 'resources/octavia/lb';
export class PoolStep extends Base {
get title() {
@ -29,10 +30,9 @@ export class PoolStep extends Base {
return true;
}
get defaultValue() {
return {
pool_protocol: 'TCP',
};
get filterOptions() {
const { context: { listener_protocol = '' } = {} } = this.props;
return poolProtocols.filter((it) => listener_protocol.includes(it.label));
}
allowed = () => Promise.resolve();
@ -76,12 +76,12 @@ export class PoolStep extends Base {
name: 'pool_protocol',
label: t('Pool Protocol'),
type: 'select',
options: [
{
label: 'TCP',
value: 'TCP',
},
],
options: this.filterOptions,
onChange: () => {
this.updateContext({
health_type: '',
});
},
required: true,
},
];

View File

@ -36,6 +36,9 @@ import VPN from '../containers/VPN';
import IPsecSiteConnectionDetail from '../containers/VPN/IPsecSiteConnection/Detail';
import SecurityGroups from '../containers/SecurityGroup';
import SecurityGroupDetail from '../containers/SecurityGroup/Detail';
import Certificate from '../containers/Certificate';
import CertificateDetailContainer from '../containers/Certificate/Detail/Container';
import CertificateDetailSecret from '../containers/Certificate/Detail/Secret';
const PATH = '/network';
export default [
@ -157,6 +160,32 @@ export default [
component: ListenerDetail,
exact: true,
},
{ path: `${PATH}/certificate`, component: Certificate, exact: true },
{
path: `${PATH}/certificate-container/detail/:id`,
component: CertificateDetailContainer,
exact: true,
},
{
path: `${PATH}/certificate-secret/detail/:id`,
component: CertificateDetailSecret,
exact: true,
},
{
path: `${PATH}/certificate-admin`,
component: Certificate,
exact: true,
},
{
path: `${PATH}/certificate-container-admin/detail/:id`,
component: CertificateDetailContainer,
exact: true,
},
{
path: `${PATH}/certificate-secret-admin/detail/:id`,
component: CertificateDetailSecret,
exact: true,
},
{ path: `${PATH}/vpn`, component: VPN, exact: true },
{
path: `${PATH}/ipsec-site-connection/detail/:id`,

View File

@ -15,3 +15,105 @@ export const provisioningStatusCodes = {
PENDING_UPDATE: t('Pending Update'),
PENDING_DELETE: t('Pending Delete'),
};
export const certificateMode = {
SERVER: t('Server Certificate'),
CA: t('CA Certificate'),
};
export const certificateStatus = {
ACTIVE: t('Active'),
ERROR: t('Error'),
};
export const certificateColumns = [
{
title: t('Name'),
dataIndex: 'name',
},
{
title: t('Certificate Type'),
dataIndex: 'mode',
render: (value) => certificateMode[value] || value,
},
{
title: t('Expires At'),
dataIndex: 'expiration',
valueRender: 'toLocalTime',
},
{
title: t('Domain Name'),
dataIndex: 'algorithm',
render: (value) => value || '-',
},
{
title: t('Status'),
dataIndex: 'status',
render: (value) => certificateStatus[value] || value,
},
{
title: t('Created At'),
dataIndex: 'created',
valueRender: 'toLocalTime',
},
];
export const sslParseMethod = [
{
label: t('One-way authentication'),
value: 'one-way',
},
{
label: t('Two-way authentication'),
value: 'two-way',
},
];
export const listenerProtocols = [
{
label: 'HTTP',
value: 'HTTP',
},
{
label: 'TCP',
value: 'TCP',
},
{
label: 'TERMINATED_HTTPS',
value: 'TERMINATED_HTTPS',
},
{
label: 'UDP',
value: 'UDP',
},
];
export const poolProtocols = [
{
label: 'HTTP',
value: 'HTTP',
},
{
label: 'TCP',
value: 'TCP',
},
{
label: 'UDP',
value: 'UDP',
},
];
export const healthProtocols = [
{
label: 'HTTP',
value: 'HTTP',
},
{
label: 'TCP',
value: 'TCP',
},
{
label: 'UDP',
value: 'UDP-CONNECT',
},
];

View File

@ -0,0 +1,184 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Base from 'stores/base';
import client from 'client';
import { action } from 'mobx';
import { SecretsStore } from './secrets';
export class ContainersStore extends Base {
get client() {
return client.barbican.containers;
}
get payloadClient() {
return client.barbican.secrets.payload;
}
get fetchListByLimit() {
return true;
}
get secretStore() {
// Not globalSecretsStore here
return new SecretsStore();
}
updateMarkerParams = (limit, offset) => ({
limit,
offset,
});
async requestListAllByLimit(params, limit) {
let hasNext = true;
let data = [];
while (hasNext) {
const offset = data.length || '';
// eslint-disable-next-line no-await-in-loop
const result = await this.requestListByMarker(params, limit, offset);
const items = this.getListDataFromResult(result);
data = [...data, ...items];
if (limit >= result.total || offset >= result.total) {
hasNext = false;
}
}
return data;
}
async listDidFetch(items) {
if (items.length === 0) return items;
const secrets = await this.secretStore.fetchList({ mode: 'SERVER' });
const newItems = items.map((it) => {
const { container_ref = '', secret_refs = [] } = it;
const [, uuid] = container_ref.split('/containers/');
if (secret_refs.length === 0) {
it.hidden = true;
} else {
// Filter available secrets
secret_refs.forEach((secret) => {
const { secret_ref = '' } = secret;
const [, secretId] = secret_ref.split('/secrets/');
const theSecret = secrets.find((s) => s.id === secretId);
if (theSecret) {
Object.assign(secret, { secret_info: theSecret });
Object.assign(it, {
algorithm: theSecret.algorithm,
mode: theSecret.mode,
expiration: theSecret.expiration,
});
} else {
it.hidden = true;
}
});
}
return {
...it,
id: uuid,
};
});
return newItems.filter((it) => it.hidden !== true);
}
async detailDidFetch(item) {
const { secret_refs = [] } = item;
const secrets = await this.secretStore.fetchList({ mode: 'SERVER' });
const secretIds = [];
// Filter available secrets
secret_refs.forEach(async (secret) => {
const { secret_ref = '' } = secret;
const [, secretId] = secret_ref.split('/secrets/');
const theSecret = secrets.find((s) => s.id === secretId);
if (theSecret) {
secretIds.push(theSecret.id);
Object.assign(secret, { secret_info: theSecret });
Object.assign(item, {
algorithm: theSecret.algorithm,
mode: theSecret.mode,
expiration: theSecret.expiration,
});
}
});
// Fetch secrets payload
const payloads = await Promise.all(
secretIds.map((id) =>
this.payloadClient.list(id, {}, { headers: { Accept: 'text/plain' } })
)
);
(payloads || []).forEach((it, index) => {
secret_refs[index].secret_info.payload = it;
});
return item;
}
@action
async create(values) {
// Create Secret
const commonData = {
name: values.name,
mode: values.mode,
payload_content_type: 'text/plain',
secret_type: 'certificate',
expiration: values.expiration,
algorithm: values.domain,
};
const contentData = {
...commonData,
payload: values.certificate,
};
const createSecretArr = [this.secretStore.create(contentData)];
if (values.mode === 'SERVER') {
const privateKeyData = {
...commonData,
payload: values.private_key,
};
createSecretArr.push(this.secretStore.create(privateKeyData));
}
const [content, privateKey] = await Promise.all(createSecretArr);
// Create Containers
const secretRefs = [
{
name: 'certificate',
secret_ref: content.secret_ref,
},
];
if (privateKey) {
secretRefs.push({
name: 'private_key',
secret_ref: privateKey.secret_ref,
});
}
const data = {
type: 'certificate',
name: values.name,
secret_refs: secretRefs,
};
return this.client.create(data);
}
@action
delete = async (data) => {
const { id, secret_refs = [] } = data;
await Promise.all(
secret_refs.map((it) => {
const { secret_ref = '' } = it;
const [, secretId] = secret_ref.split('/secrets/');
return this.secretStore.delete(secretId);
})
);
return this.submitting(this.client.delete(id));
};
}
const globalContainersStore = new ContainersStore();
export default globalContainersStore;

View File

@ -0,0 +1,94 @@
// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Base from 'stores/base';
import client from 'client';
import { action } from 'mobx';
export class SecretsStore extends Base {
get client() {
return client.barbican.secrets;
}
get payloadClient() {
return client.barbican.secrets.payload;
}
get fetchListByLimit() {
return true;
}
get paramsFunc() {
return (params) => ({
...params,
});
}
updateMarkerParams = (limit, offset) => ({
limit,
offset,
});
async requestListAllByLimit(params, limit) {
let hasNext = true;
let data = [];
while (hasNext) {
const offset = data.length || '';
// eslint-disable-next-line no-await-in-loop
const result = await this.requestListByMarker(params, limit, offset);
const items = this.getListDataFromResult(result);
data = [...data, ...items];
if (limit >= result.total || offset >= result.total) {
hasNext = false;
}
}
return data;
}
listDidFetch(items) {
if (items.length === 0) return items;
return items.map((it) => {
const { secret_ref = '' } = it;
const [, uuid] = secret_ref.split('/secrets/');
return {
...it,
id: uuid,
};
});
}
@action
async fetchDetail({ id, silent }) {
if (!silent) {
this.isLoading = true;
}
const [item, payload] = await Promise.all([
this.client.show(id, {}, { headers: { Accept: 'application/json' } }),
this.payloadClient.list(id, {}, { headers: { Accept: 'text/plain' } }),
]);
item.payload = payload;
const detail = this.mapper(item || {});
this.detail = detail;
this.isLoading = false;
return detail;
}
@action
async create(data) {
return this.client.create(data);
}
}
const globalSecretsStore = new SecretsStore();
export default globalSecretsStore;