|
|
|
@ -4,7 +4,7 @@ import { CommandBarButton, Image, ThemeProvider, Link, Toggle, TooltipHost, Pivo
|
|
|
|
|
import { AzureThemeLight, AzureThemeDark } from '@fluentui/azure-themes';
|
|
|
|
|
import { mergeStyles, mergeStyleSets } from '@fluentui/merge-styles';
|
|
|
|
|
|
|
|
|
|
import {Presets, SeparatorStyle} from './presets'
|
|
|
|
|
import { Presets, SeparatorStyle } from './presets'
|
|
|
|
|
|
|
|
|
|
import NetworkTab from './networkTab'
|
|
|
|
|
import AddonsTab from './addonsTab'
|
|
|
|
@ -38,16 +38,16 @@ function Header({ presets, setPresets, selectedPreset, featureFlag }) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<nav role="menubar">
|
|
|
|
|
<nav role="menubar">
|
|
|
|
|
|
|
|
|
|
<div style={{ width: "100%" }}>
|
|
|
|
|
|
|
|
|
|
<div style={{display: "inline-block", padding: "11px 12px 0px"}}>
|
|
|
|
|
<div style={{ display: "inline-block", padding: "11px 12px 0px" }}>
|
|
|
|
|
<Link className="navbar-brand no-outline" >
|
|
|
|
|
<Image src="aks.svg" height="33px" />
|
|
|
|
|
</Link>
|
|
|
|
|
<Text nowrap variant="xLarge" className={titleClass} >AKS Construction <span style={{"color": "red"}}>helper</span></Text>
|
|
|
|
|
<Text className={titleClass} style={{"marginTop": "6px", "marginLeft": "20px"}}>Documentation and CI/CD samples are in the <a href="https://github.com/Azure/AKS-Construction" target="_blank" rel="noopener noreferrer">GitHub Repository</a></Text>
|
|
|
|
|
<Text nowrap variant="xLarge" className={titleClass} >AKS Construction <span style={{ "color": "red" }}>Helper</span></Text>
|
|
|
|
|
<Text className={titleClass} style={{ "marginTop": "6px", "marginLeft": "20px" }}>Documentation and CI/CD samples are in the <a href="https://github.com/Azure/AKS-Construction" target="_blank" rel="noopener noreferrer">GitHub Repository</a> and at the <a href="https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/app-platform/aks/landing-zone-accelerator" target="_blank" rel="noopener noreferrer">AKS Landing Zone Accelerator</a> docs</Text>
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{ display: "inline-block", float: "right" }}>
|
|
|
|
|
|
|
|
|
@ -59,7 +59,8 @@ function Header({ presets, setPresets, selectedPreset, featureFlag }) {
|
|
|
|
|
disabled: presets[p].disabled,
|
|
|
|
|
iconProps: { iconName: presets[p].icon },
|
|
|
|
|
onClick: () => setPresets(p)
|
|
|
|
|
} })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}} text={presets[selectedPreset].title} disabled={false} checked={true}
|
|
|
|
|
styles={{ root: { "vertical-align": "top", padding: "11px 12px 13px", border: "2px solid transparent", background: "transparent" }, label: { color: "#0067b8", fontWeight: "600", fontSize: "15px", lineHeight: "1.3" } }} />
|
|
|
|
@ -75,17 +76,17 @@ function Header({ presets, setPresets, selectedPreset, featureFlag }) {
|
|
|
|
|
function Header2({ presets, setPresets, selectedPreset, featureFlag }) {
|
|
|
|
|
return (
|
|
|
|
|
<Stack horizontal tokens={{ childrenGap: 10 }}>
|
|
|
|
|
<img id="aksLogo" src="aks.svg" alt="Kubernetes Service" style={{ }}></img>
|
|
|
|
|
<img id="aksLogo" src="aks.svg" alt="Kubernetes Service" style={{}}></img>
|
|
|
|
|
<Stack tokens={{ padding: 10, maxWidth: 700 }} className="intro">
|
|
|
|
|
<Text variant="xLarge">AKS Construction helper</Text>
|
|
|
|
|
<Text variant="large" styles={{ root: { marginBottom: '6px'} }}>Generate Azure deployment assets by providing your requirements to quickly create a full operational environment from best practice guidance.</Text>
|
|
|
|
|
<Text variant="large" styles={{ root: { marginBottom: '6px' } }}>Generate Azure deployment assets by providing your requirements to quickly create a full operational environment from best practice guidance.</Text>
|
|
|
|
|
<Text variant="medium" >Documentation and CI/CD samples are in the <a href="https://github.com/Azure/AKS-Construction" target="_blank" rel="noopener noreferrer">GitHub Repository</a></Text>
|
|
|
|
|
</Stack>
|
|
|
|
|
<Stack grow={1} tokens={{ padding: 10 }} >
|
|
|
|
|
|
|
|
|
|
<ChoiceGroup
|
|
|
|
|
defaultSelectedKey={selectedPreset}
|
|
|
|
|
options={Object.keys(presets).map(p => { return { key: p, text: presets[p].title, disabled: presets[p].disabled, iconProps: { iconName: presets[p].icon } } })}
|
|
|
|
|
options={Object.keys(presets).map(p => { return { key: p, text: presets[p].title, disabled: presets[p].disabled, iconProps: { iconName: presets[p].icon } } })}
|
|
|
|
|
onChange={(ev, { key }) => setPresets(key)}
|
|
|
|
|
>
|
|
|
|
|
</ChoiceGroup>
|
|
|
|
@ -198,7 +199,7 @@ export default function PortalNav({ config }) {
|
|
|
|
|
|
|
|
|
|
setUrlParams((currentUrlParams) => {
|
|
|
|
|
|
|
|
|
|
currentUrlParams.set(sectionKey, cardKey)
|
|
|
|
|
currentUrlParams.set(sectionKey, cardKey)
|
|
|
|
|
return currentUrlParams
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
@ -235,13 +236,13 @@ export default function PortalNav({ config }) {
|
|
|
|
|
return response.json();
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
console.log(`useEffect Get template versions`)
|
|
|
|
|
const releases = res.filter(rel => rel.assets.find(a => a.name === 'main.json') && rel.assets.find(a => a.name === 'postdeploy.sh') && rel.assets.find(a => a.name === 'dependencies.json') && rel.draft === false).map((rel, i) => {
|
|
|
|
|
const releases = res.filter(rel => rel.assets.find(a => a.name === 'main.json') && rel.assets.find(a => a.name === 'postdeploy.sh') && rel.assets.find(a => a.name === 'dependencies.json') && rel.draft === false).map((rel, i) => {
|
|
|
|
|
return {
|
|
|
|
|
key: rel.tag_name,
|
|
|
|
|
text: `${rel.tag_name}${i === 0 ? ' (latest)' : ''}`,
|
|
|
|
|
main_url: rel.assets.find(a => a.name === 'main.json').browser_download_url,
|
|
|
|
|
post_url: rel.assets.find(a => a.name === 'postdeploy.sh').browser_download_url,
|
|
|
|
|
base_download_url: rel.assets.find(a => a.name === 'main.json').browser_download_url.replace('/main.json','')
|
|
|
|
|
base_download_url: rel.assets.find(a => a.name === 'main.json').browser_download_url.replace('/main.json', '')
|
|
|
|
|
}
|
|
|
|
|
}).concat(defaults.deploy.templateVersions)
|
|
|
|
|
//console.log (releases)
|
|
|
|
@ -301,14 +302,14 @@ export default function PortalNav({ config }) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//maintains the current config in querystring for easy bookmarking
|
|
|
|
|
window.history.replaceState(null, null, "?"+urlParams.toString())
|
|
|
|
|
window.history.replaceState(null, null, "?" + urlParams.toString())
|
|
|
|
|
|
|
|
|
|
setTabValues((p) => {
|
|
|
|
|
return {
|
|
|
|
|
...p,
|
|
|
|
|
[tab]: {
|
|
|
|
|
...p[tab],
|
|
|
|
|
...updatevals
|
|
|
|
|
...p[tab],
|
|
|
|
|
...updatevals
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
@ -336,7 +337,7 @@ export default function PortalNav({ config }) {
|
|
|
|
|
invalidFn('cluster', 'keyVaultKmsByoKeyId', cluster.keyVaultKms === "byoprivate" && !cluster.keyVaultKmsByoKeyId.match('https:\/\/[^]+.vault.azure.net/keys/[^ ]+/[^ ]+$'), 'Enter valid KeyVault Versioned Key ID (https://YOURVAULTNAME.vault.azure.net/keys/YOURKEYNAME/KEYVERSIONSTRING)')
|
|
|
|
|
invalidFn('cluster', 'keyVaultKmsByoRG', cluster.keyVaultKms === "byoprivate" && !cluster.keyVaultKmsByoRG, 'Enter existing KeyVault Resource Group Name')
|
|
|
|
|
invalidFn('addons', 'dnsZoneId', addons.dns && !addons.dnsZoneId.match('^/subscriptions/[^/ ]+/resourceGroups/[^/ ]+/providers/Microsoft.Network/(dnszones|privateDnsZones)/[^/ ]+$'), 'Enter valid Azure Public or Private DNS Zone resourceId')
|
|
|
|
|
invalidFn('cluster', 'dnsApiPrivateZoneId', cluster.apisecurity === 'private' && cluster.privateClusterDnsMethod==='privateDnsZone' && !cluster.dnsApiPrivateZoneId.match('^/subscriptions/[^/ ]+/resourceGroups/[^/ ]+/providers/Microsoft.Network/privateDnsZones/[^/ ]+.azmk8s.io$'), 'Enter valid Azure Private DNS Zone resourceId')
|
|
|
|
|
invalidFn('cluster', 'dnsApiPrivateZoneId', cluster.apisecurity === 'private' && cluster.privateClusterDnsMethod === 'privateDnsZone' && !cluster.dnsApiPrivateZoneId.match('^/subscriptions/[^/ ]+/resourceGroups/[^/ ]+/providers/Microsoft.Network/privateDnsZones/[^/ ]+.azmk8s.io$'), 'Enter valid Azure Private DNS Zone resourceId')
|
|
|
|
|
invalidFn('addons', 'certEmail', addons.certMan && !addons.certEmail.match('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$'), 'Enter valid email for certificate generation')
|
|
|
|
|
invalidFn('addons', 'kvId', addons.csisecret === "akvExist" && !addons.kvId.match('^/subscriptions/[^/ ]+/resourceGroups/[^/ ]+/providers/Microsoft.KeyVault/vaults/[^/ ]+$'), 'Enter valid Azure KeyVault resourceId')
|
|
|
|
|
invalidFn('addons', 'appgw_privateIpAddress', addons.ingress === "appgw" && addons.appgw_privateIp && !addons.appgw_privateIpAddress.match('^(?:[0-9]{1,3}.){3}[0-9]{1,3}$'), 'Enter valid IP address')
|
|
|
|
@ -348,9 +349,9 @@ export default function PortalNav({ config }) {
|
|
|
|
|
net.vnet_opt === "byo" ?
|
|
|
|
|
'Please de-select, when using Bring your own VNET, configure a firewall as part of your own VNET setup, (in a subnet or peered network)'
|
|
|
|
|
:
|
|
|
|
|
'This template can only deploy Azure Firewall in single VNET with Custom Networking' )
|
|
|
|
|
'This template can only deploy Azure Firewall in single VNET with Custom Networking')
|
|
|
|
|
invalidFn('net', 'aksOutboundTrafficType', (net.aksOutboundTrafficType === 'managedNATGateway' && net.vnet_opt !== "default") || (net.aksOutboundTrafficType === 'userAssignedNATGateway' && net.vnet_opt === "default"), 'When using Managed Nat Gateway, only default networking is supported. For other networking options, use Assigned NAT Gateway')
|
|
|
|
|
invalidFn('deploy', 'apiips', cluster.apisecurity === 'whitelist' && deploy.apiips.length < 7, 'Enter an IP/CIDR, or disable API Security in \'Cluster Details\' tab')
|
|
|
|
|
invalidFn('deploy', 'apiips', cluster.apisecurity === 'whitelist' && deploy.apiips.length < 7, 'Enter an IP/CIDR, or select \'Public IP with no IP restrictions\' in the \'Cluster API Server Security\' section of the \'Cluster Details\' tab')
|
|
|
|
|
invalidFn('deploy', 'clusterName', !deploy.clusterName || deploy.clusterName.match(/^[a-z0-9][_\-a-z0-9]+[a-z0-9]$/i) === null || deploy.clusterName.length > 19, 'Enter valid cluster name')
|
|
|
|
|
|
|
|
|
|
invalidFn('deploy', 'githubrepo', deploy.deployItemKey === 'github' && (!deploy.githubrepo || !deploy.githubrepo.match('https://github.com/[^/ ]+/[^/ ]+$')), 'enter repo URL. eg: https://github.com/org/repo')
|
|
|
|
@ -381,11 +382,11 @@ export default function PortalNav({ config }) {
|
|
|
|
|
|
|
|
|
|
<Presets sections={sections} selectedValues={selected.values} updateSelected={updateSelected} featureFlag={featureFlag} />
|
|
|
|
|
|
|
|
|
|
<Separator styles={SeparatorStyle}><span style={{"color": "rgb(0, 103, 184)"}}>Fine tune & Deploy</span></Separator>
|
|
|
|
|
<Separator styles={SeparatorStyle}><span style={{ "color": "rgb(0, 103, 184)" }}>Fine tune & Deploy</span></Separator>
|
|
|
|
|
|
|
|
|
|
<Pivot selectedKey={pivotkey} onLinkClick={_handleLinkClick} focusZoneProps={{ 'data-testid': `portalnav-Pivot` }}>
|
|
|
|
|
<PivotItem headerText={tabLabels.deploy} itemKey="deploy" onRenderItemLink={(a, b) => _customRenderer('deploy', a, b)}>
|
|
|
|
|
<DeployTab defaults={defaults} tabValues={tabValues} updateFn={(field, value) => mergeState("deploy", field, value)} invalidArray={invalidArray['deploy']} invalidTabs={Object.keys(invalidArray).filter(t => invalidArray[t].length > 0).map(k => `'${tabLabels[k]}'`)} urlParams={urlParams} featureFlag={featureFlag} />
|
|
|
|
|
<DeployTab defaults={defaults} tabValues={tabValues} updateFn={(field, value) => mergeState("deploy", field, value)} invalidArray={invalidArray['deploy']} invalidTabs={Object.keys(invalidArray).filter(t => invalidArray[t].length > 0).map(k => `'${tabLabels[k]}'`)} urlParams={urlParams} featureFlag={featureFlag} />
|
|
|
|
|
</PivotItem>
|
|
|
|
|
<PivotItem headerText={tabLabels.cluster} itemKey="cluster" onRenderItemLink={(a, b) => _customRenderer('cluster', a, b)} >
|
|
|
|
|
<ClusterTab tabValues={tabValues} featureFlag={featureFlag} updateFn={(field, value) => mergeState("cluster", field, value)} invalidArray={invalidArray['cluster']} />
|
|
|
|
|