>
}
export interface DeleteInstalledChartParamsType {
diff --git a/src/components/charts/charts.util.tsx b/src/components/charts/charts.util.tsx
index be27a9d554..13c5ed7c35 100644
--- a/src/components/charts/charts.util.tsx
+++ b/src/components/charts/charts.util.tsx
@@ -117,6 +117,7 @@ export const QueryParams = {
AppStoreName: 'appStoreName',
RegistryId: 'registryId',
SearchKey: 'searchKey',
+ ChartCategoryId: 'chartCategoryId',
}
export const PaginationParams = {
diff --git a/src/components/charts/constants.ts b/src/components/charts/constants.ts
index bd38f5063c..8c38cb6012 100644
--- a/src/components/charts/constants.ts
+++ b/src/components/charts/constants.ts
@@ -34,7 +34,7 @@ export const APP_NAME_TAKEN = 'App name already taken'
export enum CHART_KEYS {
CHART_REPO = 'chart-repo',
+ CHART_CATEGORY = 'chart-category',
DEPRECATED = 'deprecated',
SEARCH = 'search',
- CLEAR = 'clear',
}
diff --git a/src/components/charts/discoverChartDetail/ChartDeploymentList.tsx b/src/components/charts/discoverChartDetail/ChartDeploymentList.tsx
index ec0015b3c3..eabd1b7331 100644
--- a/src/components/charts/discoverChartDetail/ChartDeploymentList.tsx
+++ b/src/components/charts/discoverChartDetail/ChartDeploymentList.tsx
@@ -36,7 +36,7 @@ import { Td } from '../../common'
import { Routes, URLS, ViewType, SERVER_MODE, DELETE_ACTION } from '../../../config'
import { deleteInstalledChart } from '../charts.service'
import AppNotDeployedIcon from '../../../assets/img/app-not-configured.png'
-import dots from '../../../assets/icons/appstatus/ic-menu-dots.svg'
+import { ReactComponent as Dots } from '@Icons/ic-dot.svg'
import trash from '../../../assets/icons/ic-delete.svg'
import { getAppId } from '../../v2/appDetails/k8Resource/nodeDetail/nodeDetail.api'
import ClusterNotReachableDialog from '../../common/ClusterNotReachableDialog/ClusterNotReachableDialog'
@@ -243,8 +243,8 @@ export const DeploymentRow = ({
-
-
+
+
setConfirmation(true)}>
diff --git a/src/components/charts/list/DiscoverCharts.tsx b/src/components/charts/list/DiscoverCharts.tsx
index 8c1eae5504..5adfb66f15 100644
--- a/src/components/charts/list/DiscoverCharts.tsx
+++ b/src/components/charts/list/DiscoverCharts.tsx
@@ -26,6 +26,9 @@ import {
FeatureTitleWithInfo,
ToastVariantType,
ToastManager,
+ Button,
+ ComponentSizeType,
+ ButtonVariantType,
} from '@devtron-labs/devtron-fe-common-lib'
import { Switch, Route, NavLink, useHistory, useLocation, useRouteMatch, Prompt } from 'react-router-dom'
import Tippy from '@tippyjs/react'
@@ -111,6 +114,7 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
const [appStoreName, setAppStoreName] = useState('')
const [searchApplied, setSearchApplied] = useState(false)
const [includeDeprecated, setIncludeDeprecated] = useState(0)
+ const [chartCategoryIds, setChartCategoryIds] = useState ([])
const projectsMap = mapByKey(state.projects, 'id')
const chartList: Chart[] = Array.from(state.availableCharts.values())
const isLeavingPageNotAllowed = useRef(false)
@@ -125,7 +129,7 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
const [isLoading, setIsLoading] = useState(false)
const [filteredChartList, setFilteredChartList] = useState([])
- const noChartAvailable: boolean = chartList.length > 0 || searchApplied || selectedChartRepo.length > 0
+ const noChartAvailable: boolean = chartList.length > 0 || searchApplied || selectedChartRepo.length > 0 || !!chartCategoryIds
isLeavingPageNotAllowed.current = !state.charts.reduce((acc: boolean, chart: ChartGroupEntry) => {
return (acc = acc && chart.originalValuesYaml === chart.valuesYaml)
}, true)
@@ -262,12 +266,14 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
toggleDeployModal(false)
}
+ // should be removed and use useUrlFilters instead
function initialiseFromQueryParams(chartRepoList): void {
const searchParams = new URLSearchParams(location.search)
const allChartRepoIds: string = searchParams.get(QueryParams.ChartRepoId)
const allRegistryIds: string = searchParams.get(QueryParams.RegistryId)
const deprecated: string = searchParams.get(QueryParams.IncludeDeprecated)
const appStoreName: string = searchParams.get(QueryParams.AppStoreName)
+ const chartCategoryCsv: string = searchParams.get(QueryParams.ChartCategoryId)
let chartRepoIdArray = []
let ociRegistryArray = []
if (allChartRepoIds) {
@@ -297,6 +303,8 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
}
if (deprecated) {
setIncludeDeprecated(parseInt(deprecated))
+ } else {
+ setIncludeDeprecated(0)
}
if (appStoreName) {
setSearchApplied(true)
@@ -305,6 +313,14 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
setSearchApplied(false)
setAppStoreName('')
}
+ if (chartCategoryCsv) {
+ const idsArray = chartCategoryCsv.split(',')
+ if (idsArray) {
+ setChartCategoryIds(idsArray)
+ }
+ } else {
+ setChartCategoryIds([])
+ }
}
async function callApplyFilterOnCharts(resetPage?: boolean) {
@@ -369,24 +385,25 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
/
>
)}
-
+
{state.charts.length === 0 ? (
<>
- Chart Store
+ Chart Store
{isSuperAdmin && (
- }
onClick={onChangeShowSourcePopup}
- >
-
- Source
-
+ size={ComponentSizeType.xs}
+ variant={ButtonVariantType.secondary}
+ />
)}
>
) : (
'Deploy multiple charts'
)}
-
+
{showSourcePopoUp && (
@@ -429,9 +446,7 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
return (
<>
- 0 ? 'summary-show' : ''} chart-store-header`}
- >
+ 0 ? 'summary-show' : ''}`}>
0} wrap={(children) => {children} }>
@@ -451,6 +466,8 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
selectedChartRepo={selectedChartRepo}
isGrid={isGrid}
setIsGrid={setIsGrid}
+ chartCategoryIds={chartCategoryIds}
+ setChartCategoryIds={setChartCategoryIds}
/>
)}
{state.loading || chartListLoading ? (
@@ -495,7 +512,8 @@ const DiscoverChartList = ({ isSuperAdmin }: { isSuperAdmin: boolean }) => {
{serverMode == SERVER_MODE.FULL &&
!searchApplied &&
- selectedChartRepo.length === 0 && (
+ !selectedChartRepo.length &&
+ !chartCategoryIds.length && (
Pick =
+) => Pick =
importComponentFromFELibrary('getSSHConfig', noop, 'function')
+const VirtualClusterForm = importComponentFromFELibrary('VirtualClusterForm', null, 'function')
class ClusterList extends Component {
timerRef
@@ -73,21 +72,14 @@ class ClusterList extends Component {
view: ViewType.LOADING,
clusters: [],
clusterEnvMap: {},
- isTlsConnection: false,
appCreationType: AppCreationType.Blank,
- isKubeConfigFile: false,
browseFile: false,
- isClusterDetails: false,
showEditCluster: false,
isConnectedViaProxy: false,
isConnectedViaSSHTunnel: false,
}
this.initialise = this.initialise.bind(this)
- this.toggleCheckTlsConnection = this.toggleCheckTlsConnection.bind(this)
- this.setTlsConnectionFalse = this.setTlsConnectionFalse.bind(this)
- this.toggleKubeConfigFile = this.toggleKubeConfigFile.bind(this)
this.toggleBrowseFile = this.toggleBrowseFile.bind(this)
- this.toggleClusterDetails = this.toggleClusterDetails.bind(this)
this.toggleShowEditCluster = this.toggleShowEditCluster.bind(this)
}
@@ -187,26 +179,10 @@ class ClusterList extends Component {
clearInterval(this.timerRef)
}
- toggleCheckTlsConnection() {
- this.setState({ isTlsConnection: !this.state.isTlsConnection })
- }
-
- setTlsConnectionFalse() {
- this.setState({ isTlsConnection: false })
- }
-
- toggleClusterDetails(updateClusterDetails: boolean) {
- this.setState({ isClusterDetails: updateClusterDetails })
- }
-
toggleShowEditCluster() {
this.setState({ showEditCluster: !this.state.showEditCluster })
}
- toggleKubeConfigFile(updateKubeConfigFile: boolean) {
- this.setState({ isKubeConfigFile: updateKubeConfigFile })
- }
-
toggleBrowseFile() {
this.setState({ browseFile: !this.state.browseFile })
}
@@ -250,12 +226,14 @@ class ClusterList extends Component {
}
size={ComponentSizeType.medium}
- text="Add cluster"
+ text="New Cluster"
/>
{this.state.clusters.map(
@@ -267,41 +245,16 @@ class ClusterList extends Component {
key={cluster.id || Math.random().toString(36).substr(2, 5)}
showEditCluster={this.state.showEditCluster}
toggleShowAddCluster={this.toggleShowEditCluster}
- toggleCheckTlsConnection={this.toggleCheckTlsConnection}
- setTlsConnectionFalse={this.setTlsConnectionFalse}
- isTlsConnection={this.state.isTlsConnection}
+ isTlsConnection={cluster.isTlsConnection}
prometheus_url={cluster.prometheus_url}
/>
),
)}
-
-
-
+
{
return (
{
export default withRouter(ClusterList)
const Cluster = ({
- id,
id: clusterId,
cluster_name,
insecureSkipTlsVerify,
- defaultClusterComponent,
- agentInstallationStage,
server_url,
- active,
- config: defaultConfig,
environments,
reload,
prometheus_url,
proxyUrl,
toConnectWithSSHTunnel,
sshTunnelConfig,
- isTlsConnection,
- toggleShowAddCluster,
- toggleCheckTlsConnection,
- setTlsConnectionFalse,
isVirtualCluster,
isProd,
}) => {
const [editMode, toggleEditMode] = useState(false)
const [environment, setEnvironment] = useState(null)
- const [config, setConfig] = useState(defaultConfig)
const [prometheusAuth, setPrometheusAuth] = useState(undefined)
const [showWindow, setShowWindow] = useState(false)
const [confirmation, setConfirmation] = useState(false)
- const [prometheusToggleEnabled] = useState(!!prometheus_url)
-
- const [prometheusAuthenticationType] = useState({
- type: prometheusAuth?.userName ? AuthenticationType.BASIC : AuthenticationType.ANONYMOUS,
- })
- const authenticationType = prometheusAuth?.userName ? AuthenticationType.BASIC : AuthenticationType.ANONYMOUS
const drawerRef = useRef(null)
@@ -380,189 +318,28 @@ const Cluster = ({
identifier: `cluster-list__${cluster_name}`,
})
- const isDefaultCluster = (): boolean => {
- return id == 1
+ const handleModalClose = () => {
+ toggleEditMode(false)
}
- const { state } = useForm(
- {
- cluster_name: { value: cluster_name, error: '' },
- url: { value: server_url, error: '' },
- userName: { value: prometheusAuth?.userName, error: '' },
- password: { value: prometheusAuth?.password, error: '' },
- prometheusTlsClientKey: { value: prometheusAuth?.tlsClientKey, error: '' },
- prometheusTlsClientCert: { value: prometheusAuth?.tlsClientCert, error: '' },
- proxyUrl: { value: proxyUrl, error: '' },
- sshUsername: { value: sshTunnelConfig?.user, error: '' },
- sshPassword: { value: sshTunnelConfig?.password, error: '' },
- sshAuthKey: { value: sshTunnelConfig?.authKey, error: '' },
- sshServerAddress: { value: sshTunnelConfig?.sshServerAddress, error: '' },
- isConnectedViaProxy: !!proxyUrl?.length,
- isConnectedViaSSHTunnel: toConnectWithSSHTunnel,
- tlsClientKey: { value: config.tls_key, error: '' },
- tlsClientCert: { value: config.cert_data, error: '' },
- certificateAuthorityData: { value: config.cert_auth_data, error: '' },
- token: { value: config?.bearer_token ? config.bearer_token : '', error: '' },
- endpoint: { value: prometheus_url || '', error: '' },
- authType: { value: authenticationType, error: '' },
- },
- {
- cluster_name: {
- required: true,
- validators: [
- { error: 'Name is required', regex: /^.*$/ },
- { error: "Use only lowercase alphanumeric characters, '-', '_' or '.'", regex: /^[a-z0-9-\.\_]+$/ },
- { error: "Cannot start/end with '-', '_' or '.'", regex: /^(?![-._]).*[^-._]$/ },
- { error: 'Minimum 3 and Maximum 63 characters required', regex: /^.{3,63}$/ },
- ],
- },
- url: {
- required: true,
- validator: { error: 'URL is required', regex: /^.*$/ },
- },
- proxyUrl: {
- required: false,
- validator: { error: 'Incorrect Url', regex: /^.*$/ },
- },
- toConnectWithSSHTunnel: {
- required: false,
- },
- sshUsername: {
- required: false,
- validator: {
- error: 'Username or User Identifier is required. Username cannot contain spaces or special characters other than _ and -',
- regex: /^[A-Za-z0-9_-]+$/,
- },
- },
- sshPassword: {
- required: false,
- validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
- },
- sshAuthKey: {
- required: false,
- validator: { error: 'ssh private key is required', regex: /^(?!\s*$).+/ },
- },
- sshServerAddress: {
- required: false,
- validator: { error: 'URL is required', regex: /^.*$/ },
- },
- isConnectedViaProxy: {
- required: false,
- },
- authType: {
- required: false,
- validator: { error: 'Authentication Type is required', regex: /^(?!\s*$).+/ },
- },
- userName: {
- required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
- validator: { error: 'username is required', regex: /^(?!\s*$).+/ },
- },
- password: {
- required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
- validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
- },
- tlsClientKey: {
- required: false,
- validator: { error: 'TLS Key is required', regex: /^(?!\s*$).+/ },
- },
- tlsClientCert: {
- required: false,
- validator: { error: 'TLS Certificate is required', regex: /^(?!\s*$).+/ },
- },
- prometheusTlsClientKey: {
- required: false,
- },
- prometheusTlsClientCert: {
- required: false,
- },
- certificateAuthorityData: {
- required: false,
- validator: { error: 'Certificate authority data is required', regex: /^(?!\s*$).+/ },
- },
- token:
- isDefaultCluster() || id
- ? {}
- : {
- required: true,
- validator: { error: 'token is required', regex: /[^]+/ },
- },
- endpoint: {
- required: !!prometheusToggleEnabled,
- validator: { error: 'endpoint is required', regex: /^.*$/ },
- },
- },
- onValidation,
- )
-
- const history = useHistory()
const newEnvs = useMemo(() => {
return clusterId ? [{ id: null }].concat(environments || []) : environments || []
}, [environments])
- async function handleEdit(e) {
+ async function handleEdit() {
try {
const { result } = await getCluster(clusterId)
setPrometheusAuth(result.prometheusAuth)
- setConfig({ ...result.config, ...(clusterId != 1 ? { bearer_token: DEFAULT_SECRET_PLACEHOLDER } : null) })
toggleEditMode((t) => !t)
} catch (err) {
showError(err)
}
}
- const hideClusterDrawer = (e) => {
+ const hideClusterDrawer = () => {
setShowWindow(false)
}
- async function onValidation() {
- const payload = getClusterPayload()
- const urlValue = state.url.value?.trim() ?? ''
- if (urlValue.endsWith('/')) {
- payload['server_url'] = urlValue.slice(0, -1)
- } else {
- payload['server_url'] = urlValue
- }
- const proxyUrlValue = state.proxyUrl.value?.trim() ?? ''
- if (proxyUrlValue.endsWith('/')) {
- payload.remoteConnectionConfig.proxyConfig['proxyUrl'] = proxyUrlValue.slice(0, -1)
- }
- if (state.authType.value === AuthenticationType.BASIC && prometheusToggleEnabled) {
- const isValid = state.userName?.value && state.password?.value
- if (!isValid) {
- ToastManager.showToast({
- variant: ToastVariantType.error,
- description: 'Please add both username and password',
- })
- } else {
- payload.prometheusAuth['userName'] = state.userName.value || ''
- payload.prometheusAuth['password'] = state.password.value || ''
- }
- }
- }
-
- const getClusterPayload = () => {
- return {
- id,
- cluster_name: state.cluster_name.value,
- config: {
- bearer_token:
- state.token.value && state.token.value !== DEFAULT_SECRET_PLACEHOLDER ? state.token.value : '',
- },
- active,
- prometheus_url: prometheusToggleEnabled ? state.endpoint.value : '',
- prometheusAuth: {
- userName: prometheusToggleEnabled ? state.userName.value : '',
- password: prometheusToggleEnabled ? state.password.value : '',
- tlsClientKey: prometheusToggleEnabled ? state.tlsClientKey.value : '',
- tlsClientCert: prometheusToggleEnabled ? state.tlsClientCert.value : '',
- },
- remoteConnectionConfig: getRemoteConnectionConfig(state),
- insecureSkipTlsVerify: !isTlsConnection,
- }
- }
-
- const envName: string = getEnvName(defaultClusterComponent, agentInstallationStage)
-
const renderNoEnvironmentTab = () => {
return (
@@ -586,9 +363,6 @@ const Cluster = ({
if (!clusterId) {
toggleEditMode((t) => !t)
}
- if (isTlsConnection === insecureSkipTlsVerify) {
- toggleCheckTlsConnection()
- }
}
const clusterIcon = () => {
@@ -635,9 +409,14 @@ const Cluster = ({
clusterId ? 'cluster-list--update' : 'cluster-list--create collapsed-list'
}`}
>
-
+
{!clusterId && (
@@ -774,42 +553,40 @@ const Cluster = ({
) : (
clusterId && renderNoEnvironmentTab()
)}
- {editMode && (
-
-
-
-
-
- )}
+ {editMode &&
+ (!isVirtualCluster ? (
+
+
+
+
+
+ ) : (
+
+ ))}
+
{showWindow && (
{
- return (
-
-
-
-
- Warning: Prometheus configuration will be removed and
- you won’t be able to see metrics for applications deployed in this cluster.
-
+const PrometheusWarningInfo = () => (
+
+
+
+
+ Warning: Prometheus configuration will be removed and you
+ won’t be able to see metrics for applications deployed in this cluster.
- )
-}
-
-const PrometheusRequiredFieldInfo = () => {
- return (
-
-
-
-
- Fill all the required fields OR turn off the above switch to skip configuring prometheus.
-
+
+)
+
+const PrometheusRequiredFieldInfo = () => (
+
+
+
+
+ Fill all the required fields OR turn off the above switch to skip configuring prometheus.
- )
-}
+
+)
-export default function ClusterForm({
+const ClusterForm = ({
id = null,
- cluster_name,
- server_url,
- active,
- config,
+ clusterName,
+ serverUrl,
toggleEditMode = noop,
- reload,
- prometheus_url,
+ reload = noop,
+ prometheusUrl = '',
prometheusAuth,
- defaultClusterComponent,
- proxyUrl,
- sshUsername,
- sshPassword,
- sshAuthKey,
- sshServerAddress,
- isConnectedViaProxy,
- isConnectedViaSSHTunnel,
- isTlsConnection,
- toggleCheckTlsConnection,
- setTlsConnectionFalse,
+ proxyUrl = '',
+ sshUsername = '',
+ sshPassword = '',
+ sshAuthKey = '',
+ sshServerAddress = '',
+ isConnectedViaSSHTunnel = false,
handleCloseCreateClusterForm = noop,
- toggleKubeConfigFile,
- isKubeConfigFile,
- isClusterDetails,
- toggleClusterDetails,
- isVirtualCluster,
isProd = false,
-}: ClusterFormProps) {
- const [prometheusToggleEnabled, setPrometheusToggleEnabled] = useState(!!prometheus_url)
+ FooterComponent,
+ handleModalClose = noop,
+ isTlsConnection: initialIsTlsConnection = false,
+}: ClusterFormProps & Partial ) => {
+ const [prometheusToggleEnabled, setPrometheusToggleEnabled] = useState(!!prometheusUrl)
const [prometheusAuthenticationType, setPrometheusAuthenticationType] = useState({
type: prometheusAuth?.userName ? AuthenticationType.BASIC : AuthenticationType.ANONYMOUS,
})
+ const [isTlsConnection, setIsTlsConnection] = useState(initialIsTlsConnection)
+ const [isKubeConfigFile, toggleKubeConfigFile] = useState(false)
+ const [isClusterDetails, toggleClusterDetails] = useState(false)
const authenTicationType = prometheusAuth?.userName ? AuthenticationType.BASIC : AuthenticationType.ANONYMOUS
- const isDefaultCluster = (): boolean => {
- return id == 1
+ const isConnectedViaProxy = !!proxyUrl
+
+ const toggleCheckTlsConnection = () => {
+ setIsTlsConnection((prev) => !prev)
+ }
+
+ const setTlsConnectionFalse = () => {
+ setIsTlsConnection(false)
}
+
+ const isDefaultCluster = id === 1
const [confirmation, setConfirmation] = useState(false)
const inputFileRef = useRef(null)
const [uploadState, setUploadState] = useState(UPLOAD_STATE.UPLOAD)
@@ -164,8 +158,6 @@ export default function ClusterForm({
const [selectedUserNameOptions, setSelectedUserNameOptions] = useState>({})
const [isClusterSelected, setClusterSeleceted] = useState>({})
const [selectAll, setSelectAll] = useState(false)
- const [getClusterVar, setGetClusterState] = useState(false)
- const [isVirtual, setIsVirtual] = useState(isVirtualCluster)
const [isConnectedViaProxyTemp, setIsConnectedViaProxyTemp] = useState(isConnectedViaProxy)
const [isConnectedViaSSHTunnelTemp, setIsConnectedViaSSHTunnelTemp] = useState(isConnectedViaSSHTunnel)
@@ -184,151 +176,42 @@ export default function ClusterForm({
!window._env_.K8S_CLIENT,
)
- const _remoteConnectionMethod = isConnectedViaProxyTemp
- ? RemoteConnectionType.Proxy
- : isConnectedViaSSHTunnelTemp
- ? RemoteConnectionType.SSHTunnel
- : RemoteConnectionType.Direct
- const [remoteConnectionMethod, setRemoteConnectionMethod] = useState(_remoteConnectionMethod)
- const initialSSHAuthenticationType =
- sshPassword && sshAuthKey
- ? SSHAuthenticationType.Password_And_SSH_Private_Key
- : sshAuthKey
- ? SSHAuthenticationType.SSH_Private_Key
- : SSHAuthenticationType.Password
- const [SSHConnectionType, setSSHConnectionType] = useState(initialSSHAuthenticationType)
+ const getRemoteConnectionConfigType = () => {
+ if (isConnectedViaProxyTemp) {
+ return RemoteConnectionType.Proxy
+ }
- const { state, handleOnChange, handleOnSubmit } = useForm(
- {
- cluster_name: { value: cluster_name, error: '' },
- url: { value: !id ? getServerURLFromLocalStorage(server_url) : server_url, error: '' },
- userName: { value: prometheusAuth?.userName, error: '' },
- password: { value: prometheusAuth?.password, error: '' },
- prometheusTlsClientKey: { value: prometheusAuth?.tlsClientKey, error: '' },
- prometheusTlsClientCert: { value: prometheusAuth?.tlsClientCert, error: '' },
- proxyUrl: { value: proxyUrl, error: '' },
- sshUsername: { value: sshUsername, error: '' },
- sshPassword: { value: sshPassword, error: '' },
- sshAuthKey: { value: sshAuthKey, error: '' },
- sshServerAddress: { value: sshServerAddress, error: '' },
- tlsClientKey: { value: config?.tls_key, error: '' },
- tlsClientCert: { value: config?.cert_data, error: '' },
- certificateAuthorityData: { value: config?.cert_auth_data, error: '' },
- token: { value: config?.bearer_token ? config.bearer_token : '', error: '' },
- endpoint: { value: prometheus_url || '', error: '' },
- authType: { value: authenTicationType, error: '' },
- isProd: { value: isProd.toString(), error: '' },
- },
- {
- cluster_name: {
- required: true,
- validators: [
- { error: 'Name is required', regex: /^.*$/ },
- { error: "Use only lowercase alphanumeric characters, '-', '_' or '.'", regex: /^[a-z0-9-\.\_]+$/ },
- { error: "Cannot start/end with '-', '_' or '.'", regex: /^(?![-._]).*[^-._]$/ },
- { error: 'Minimum 3 and Maximum 63 characters required', regex: /^.{3,63}$/ },
- ],
- },
- url: {
- required: true,
- validator: { error: 'URL is required', regex: /^.*$/ },
- },
- authType: {
- required: false,
- validator: { error: 'Authentication Type is required', regex: /^(?!\s*$).+/ },
- },
- userName: {
- required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
- validator: { error: 'username is required', regex: /^(?!\s*$).+/ },
- },
- password: {
- required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
- validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
- },
- prometheusTlsClientKey: {
- required: false,
- },
- prometheusTlsClientCert: {
- required: false,
- },
- proxyUrl: {
- required: RemoteConnectionRadio && proxyUrl,
- validator: {
- error: 'Please provide a valid URL. URL must start with http:// or https://',
- regex: /^(http(s)?:\/\/)[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,
- },
- },
- sshUsername: {
- required: RemoteConnectionRadio && remoteConnectionMethod === RemoteConnectionType.SSHTunnel,
- validator: {
- error: 'Username or User Identifier is required. Username cannot contain spaces or special characters other than _ and -',
- regex: /^[A-Za-z0-9_-]+$/,
- },
- },
- sshPassword: {
- required:
- RemoteConnectionRadio &&
- remoteConnectionMethod === RemoteConnectionType.SSHTunnel &&
- (SSHConnectionType === SSHAuthenticationType.Password ||
- SSHConnectionType === SSHAuthenticationType.Password_And_SSH_Private_Key),
- validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
- },
- sshAuthKey: {
- required:
- RemoteConnectionRadio &&
- remoteConnectionMethod === RemoteConnectionType.SSHTunnel &&
- (SSHConnectionType === SSHAuthenticationType.SSH_Private_Key ||
- SSHConnectionType === SSHAuthenticationType.Password_And_SSH_Private_Key),
- validator: { error: 'private key is required', regex: /^(?!\s*$).+/ },
- },
- sshServerAddress: {
- required: RemoteConnectionRadio && remoteConnectionMethod === RemoteConnectionType.SSHTunnel,
- validator:
- remoteConnectionMethod === RemoteConnectionType.SSHTunnel
- ? {
- error: 'Please provide a valid URL. URL must start with http:// or https://',
- regex: /^(http(s)?:\/\/)[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,
- }
- : { error: '', regex: /^(?!\s*$).+/ },
- },
- tlsClientKey: {
- required: id ? false : isTlsConnection,
- },
- tlsClientCert: {
- required: id ? false : isTlsConnection,
- },
- certificateAuthorityData: {
- required: id ? false : isTlsConnection,
- },
- token:
- isDefaultCluster() || id
- ? {}
- : {
- required: true,
- validator: { error: 'token is required', regex: /[^]+/ },
- },
- endpoint: {
- required: !!prometheusToggleEnabled,
- validator: { error: 'endpoint is required', regex: /^.*$/ },
- },
- },
- onValidation,
- )
+ if (isConnectedViaSSHTunnelTemp) {
+ return RemoteConnectionType.SSHTunnel
+ }
- const isGrafanaModuleInstalled = grafanaModuleStatus?.result?.status === ModuleStatus.INSTALLED
+ return RemoteConnectionType.Direct
+ }
+
+ const resolveSSHAuthType = () => {
+ if (sshPassword && sshAuthKey) {
+ return SSHAuthenticationType.Password_And_SSH_Private_Key
+ }
+
+ if (sshAuthKey) {
+ return SSHAuthenticationType.SSH_Private_Key
+ }
- const toggleGetCluster = () => {
- setGetClusterState(!getClusterVar)
+ return SSHAuthenticationType.Password
}
+ const [remoteConnectionMethod, setRemoteConnectionMethod] = useState(getRemoteConnectionConfigType)
+ const [SSHConnectionType, setSSHConnectionType] = useState(resolveSSHAuthType)
+
+ const isGrafanaModuleInstalled = grafanaModuleStatus?.result?.status === ModuleStatus.INSTALLED
+
const handleEditConfigClick = () => {
- toggleGetCluster()
- toggleKubeConfigFile(true)
+ setDataList([])
}
const getSaveClusterPayload = (dataLists: DataListType[]) => {
const saveClusterPayload: SaveClusterPayloadType[] = []
- for (const _dataList of dataLists) {
+ dataLists.forEach((_dataList) => {
if (isClusterSelected[_dataList.cluster_name]) {
const _clusterDetails: SaveClusterPayloadType = {
id: _dataList.id,
@@ -348,34 +231,34 @@ export default function ClusterForm({
}
saveClusterPayload.push(_clusterDetails)
}
- }
+ })
return saveClusterPayload
}
- async function saveClustersDetails() {
+ const saveClustersDetails = async () => {
try {
const payload = getSaveClusterPayload(dataList)
await saveClusters(payload).then((response) => {
- const _clusterList = response.result.map((_clusterSaveDetails, index) => {
+ const _clusterList = response.result.map((_clusterSaveDetails) => {
let status
let message
if (
- _clusterSaveDetails['errorInConnecting'].length === 0 &&
- _clusterSaveDetails['clusterUpdated'] === false
+ _clusterSaveDetails.errorInConnecting.length === 0 &&
+ _clusterSaveDetails.clusterUpdated === false
) {
status = 'Added'
message = 'Cluster Added'
- } else if (_clusterSaveDetails['clusterUpdated'] === true) {
+ } else if (_clusterSaveDetails.clusterUpdated === true) {
status = 'Updated'
message = 'Cluster Updated'
} else {
status = 'Failed'
- message = _clusterSaveDetails['errorInConnecting']
+ message = _clusterSaveDetails.errorInConnecting
}
return {
- clusterName: _clusterSaveDetails['cluster_name'],
+ clusterName: _clusterSaveDetails.cluster_name,
status,
message,
}
@@ -389,59 +272,61 @@ export default function ClusterForm({
}
}
- function YAMLtoJSON(saveYamlData) {
+ const YAMLtoJSON = (yamlString: string) => {
try {
- const obj = YAML.parse(saveYamlData)
+ const obj = YAML.parse(yamlString)
const jsonStr = JSON.stringify(obj)
return jsonStr
- } catch (error) {}
+ } catch {
+ noop()
+ return ''
+ }
}
- function isCheckboxDisabled() {
+ const isCheckboxDisabled = () => {
const clusters = Object.values(selectedUserNameOptions)
if (clusters.length === 0) {
return true
}
- return clusters.every((cluster) => {
- return cluster.errorInConnecting !== 'cluster-already-exists' && cluster.errorInConnecting.length > 0
- })
+ return clusters.every(
+ (cluster) => cluster.errorInConnecting !== 'cluster-already-exists' && cluster.errorInConnecting.length > 0,
+ )
}
- async function validateClusterDetail() {
+ const validateClusterDetail = async () => {
try {
const payload = { config: YAMLtoJSON(saveYamlData) }
await validateCluster(payload).then((response) => {
const defaultUserNameSelections: Record = {}
const _clusterSelections: Record = {}
setDataList([
- ...Object.values(response.result).map((_cluster) => {
- const _userInfoList = [...Object.values(_cluster['userInfos'] as UserDetails[])]
- defaultUserNameSelections[_cluster['cluster_name']] = {
+ ...Object.values(response.result).map((_cluster) => {
+ const _userInfoList = [...Object.values(_cluster.userInfos as UserDetails[])]
+ defaultUserNameSelections[_cluster.cluster_name] = {
label: _userInfoList[0].userName,
value: _userInfoList[0].userName,
errorInConnecting: _userInfoList[0].errorInConnecting,
config: _userInfoList[0].config,
}
- _clusterSelections[_cluster['cluster_name']] = false
+ _clusterSelections[_cluster.cluster_name] = false
return {
- cluster_name: _cluster['cluster_name'],
+ cluster_name: _cluster.cluster_name,
userInfos: _userInfoList,
- server_url: _cluster['server_url'],
- active: _cluster['active'],
- defaultClusterComponent: _cluster['defaultClusterComponent'],
- insecureSkipTlsVerify: _cluster['insecureSkipTlsVerify'],
- id: _cluster['id'],
- remoteConnectionConfig: _cluster['remoteConnectionConfig'],
+ server_url: _cluster.server_url,
+ active: _cluster.active,
+ defaultClusterComponent: _cluster.defaultClusterComponent,
+ insecureSkipTlsVerify: _cluster.insecureSkipTlsVerify,
+ id: _cluster.id,
+ remoteConnectionConfig: _cluster.remoteConnectionConfig,
}
}),
])
setSelectedUserNameOptions(defaultUserNameSelections)
setClusterSeleceted(_clusterSelections)
setLoadingState(false)
- toggleGetCluster()
setValidationError(false)
})
} catch (err: any) {
@@ -464,47 +349,49 @@ export default function ClusterForm({
}
}
- const getClusterPayload = () => {
- return {
- id,
- insecureSkipTlsVerify: !isTlsConnection,
- cluster_name: state.cluster_name.value,
- config: {
- bearer_token:
- state.token.value && state.token.value !== DEFAULT_SECRET_PLACEHOLDER
- ? state.token.value.trim()
- : '',
- tls_key: state.tlsClientKey.value,
- cert_data: state.tlsClientCert.value,
- cert_auth_data: state.certificateAuthorityData.value,
- },
- isProd: state.isProd.value === 'true',
- active,
- remoteConnectionConfig: getRemoteConnectionConfig(state, remoteConnectionMethod, SSHConnectionType),
- prometheus_url: prometheusToggleEnabled ? state.endpoint.value : '',
- prometheusAuth: {
- userName:
- prometheusToggleEnabled && state.authType.value === AuthenticationType.BASIC
- ? state.userName.value
- : '',
- password:
- prometheusToggleEnabled && state.authType.value === AuthenticationType.BASIC
- ? state.password.value
- : '',
- tlsClientKey: prometheusToggleEnabled ? state.prometheusTlsClientKey.value : '',
- tlsClientCert: prometheusToggleEnabled ? state.prometheusTlsClientCert.value : '',
- isAnonymous: state.authType.value === AuthenticationType.ANONYMOUS,
- },
- }
+ const setRemoteConnectionFalse = () => {
+ setIsConnectedViaProxyTemp(false)
+ setIsConnectedViaSSHTunnelTemp(false)
}
- async function onValidation() {
- const payload = getClusterPayload()
+ const getClusterPayload = (state) => ({
+ id,
+ insecureSkipTlsVerify: !isTlsConnection,
+ cluster_name: state.cluster_name.value,
+ config: {
+ bearer_token:
+ state.token.value && state.token.value !== DEFAULT_SECRET_PLACEHOLDER ? state.token.value.trim() : '',
+ tls_key: state.tlsClientKey.value,
+ cert_data: state.tlsClientCert.value,
+ cert_auth_data: state.certificateAuthorityData.value,
+ },
+ isProd: state.isProd.value === 'true',
+ active: true,
+ remoteConnectionConfig: getRemoteConnectionConfig(state, remoteConnectionMethod, SSHConnectionType),
+ prometheus_url: prometheusToggleEnabled ? state.endpoint.value : '',
+ prometheusAuth: {
+ userName:
+ prometheusToggleEnabled && state.authType.value === AuthenticationType.BASIC
+ ? state.userName.value
+ : '',
+ password:
+ prometheusToggleEnabled && state.authType.value === AuthenticationType.BASIC
+ ? state.password.value
+ : '',
+ tlsClientKey: prometheusToggleEnabled ? state.prometheusTlsClientKey.value : '',
+ tlsClientCert: prometheusToggleEnabled ? state.prometheusTlsClientCert.value : '',
+ isAnonymous: state.authType.value === AuthenticationType.ANONYMOUS,
+ },
+ server_url: '',
+ })
+
+ const onValidation = async (state) => {
+ const payload = getClusterPayload(state)
const urlValue = state.url.value?.trim() ?? ''
if (urlValue.endsWith('/')) {
- payload['server_url'] = urlValue.slice(0, -1)
+ payload.server_url = urlValue.slice(0, -1)
} else {
- payload['server_url'] = urlValue
+ payload.server_url = urlValue
}
if (remoteConnectionMethod === RemoteConnectionType.Proxy) {
let proxyUrlValue = state.proxyUrl?.value?.trim() ?? ''
@@ -525,16 +412,16 @@ export default function ClusterForm({
})
return
}
- payload.prometheusAuth['userName'] = state.userName.value || ''
- payload.prometheusAuth['password'] = state.password.value || ''
- payload.prometheusAuth['tlsClientKey'] = state.prometheusTlsClientKey.value || ''
- payload.prometheusAuth['tlsClientCert'] = state.prometheusTlsClientCert.value || ''
+ payload.prometheusAuth.userName = state.userName.value || ''
+ payload.prometheusAuth.password = state.password.value || ''
+ payload.prometheusAuth.tlsClientKey = state.prometheusTlsClientKey.value || ''
+ payload.prometheusAuth.tlsClientCert = state.prometheusTlsClientCert.value || ''
}
if (isTlsConnection) {
if (state.tlsClientKey.value || state.tlsClientCert.value || state.certificateAuthorityData.value) {
- payload.config['tls_key'] = state.tlsClientKey.value || ''
- payload.config['cert_data'] = state.tlsClientCert.value || ''
- payload.config['cert_auth_data'] = state.certificateAuthorityData.value || ''
+ payload.config.tls_key = state.tlsClientKey.value || ''
+ payload.config.cert_data = state.tlsClientCert.value || ''
+ payload.config.cert_auth_data = state.certificateAuthorityData.value || ''
}
}
@@ -558,20 +445,138 @@ export default function ClusterForm({
}
}
+ const { state, handleOnChange, handleOnSubmit } = useForm(
+ {
+ cluster_name: { value: clusterName, error: '' },
+ url: { value: !id ? getServerURLFromLocalStorage(serverUrl) : serverUrl, error: '' },
+ userName: { value: prometheusAuth?.userName, error: '' },
+ password: { value: prometheusAuth?.password, error: '' },
+ prometheusTlsClientKey: { value: prometheusAuth?.tlsClientKey, error: '' },
+ prometheusTlsClientCert: { value: prometheusAuth?.tlsClientCert, error: '' },
+ proxyUrl: { value: proxyUrl, error: '' },
+ sshUsername: { value: sshUsername, error: '' },
+ sshPassword: { value: sshPassword, error: '' },
+ sshAuthKey: { value: sshAuthKey, error: '' },
+ sshServerAddress: { value: sshServerAddress, error: '' },
+ tlsClientKey: { value: undefined, error: '' },
+ tlsClientCert: { value: undefined, error: '' },
+ certificateAuthorityData: { value: undefined, error: '' },
+ token: { value: '', error: '' },
+ endpoint: { value: prometheusUrl || '', error: '' },
+ authType: { value: authenTicationType, error: '' },
+ isProd: { value: isProd.toString(), error: '' },
+ },
+ {
+ cluster_name: {
+ required: true,
+ validators: [
+ { error: 'Name is required', regex: /^.*$/ },
+ { error: "Use only lowercase alphanumeric characters, '-', '_' or '.'", regex: /^[a-z0-9-._]+$/ },
+ { error: "Cannot start/end with '-', '_' or '.'", regex: /^(?![-._]).*[^-._]$/ },
+ { error: 'Minimum 3 and Maximum 63 characters required', regex: /^.{3,63}$/ },
+ ],
+ },
+ url: {
+ required: true,
+ validator: { error: 'URL is required', regex: /^.*$/ },
+ },
+ authType: {
+ required: false,
+ validator: { error: 'Authentication Type is required', regex: /^(?!\s*$).+/ },
+ },
+ userName: {
+ required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
+ validator: { error: 'username is required', regex: /^(?!\s*$).+/ },
+ },
+ password: {
+ required: !!(prometheusToggleEnabled && prometheusAuthenticationType.type === AuthenticationType.BASIC),
+ validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
+ },
+ prometheusTlsClientKey: {
+ required: false,
+ },
+ prometheusTlsClientCert: {
+ required: false,
+ },
+ proxyUrl: {
+ required: RemoteConnectionRadio && proxyUrl,
+ validator: {
+ error: 'Please provide a valid URL. URL must start with http:// or https://',
+ regex: /^(http(s)?:\/\/)[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/,
+ },
+ },
+ sshUsername: {
+ required: RemoteConnectionRadio && remoteConnectionMethod === RemoteConnectionType.SSHTunnel,
+ validator: {
+ error: 'Username or User Identifier is required. Username cannot contain spaces or special characters other than _ and -',
+ regex: /^[A-Za-z0-9_-]+$/,
+ },
+ },
+ sshPassword: {
+ required:
+ RemoteConnectionRadio &&
+ remoteConnectionMethod === RemoteConnectionType.SSHTunnel &&
+ (SSHConnectionType === SSHAuthenticationType.Password ||
+ SSHConnectionType === SSHAuthenticationType.Password_And_SSH_Private_Key),
+ validator: { error: 'password is required', regex: /^(?!\s*$).+/ },
+ },
+ sshAuthKey: {
+ required:
+ RemoteConnectionRadio &&
+ remoteConnectionMethod === RemoteConnectionType.SSHTunnel &&
+ (SSHConnectionType === SSHAuthenticationType.SSH_Private_Key ||
+ SSHConnectionType === SSHAuthenticationType.Password_And_SSH_Private_Key),
+ validator: { error: 'private key is required', regex: /^(?!\s*$).+/ },
+ },
+ sshServerAddress: {
+ required: RemoteConnectionRadio && remoteConnectionMethod === RemoteConnectionType.SSHTunnel,
+ validator:
+ remoteConnectionMethod === RemoteConnectionType.SSHTunnel
+ ? {
+ error: 'Please provide a valid URL. URL must start with http:// or https://',
+ regex: /^(http(s)?:\/\/)[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/,
+ }
+ : { error: '', regex: /^(?!\s*$).+/ },
+ },
+ tlsClientKey: {
+ required: id ? false : isTlsConnection,
+ },
+ tlsClientCert: {
+ required: id ? false : isTlsConnection,
+ },
+ certificateAuthorityData: {
+ required: id ? false : isTlsConnection,
+ },
+ token:
+ isDefaultCluster || id
+ ? {}
+ : {
+ required: true,
+ validator: { error: 'token is required', regex: /[^]+/ },
+ },
+ endpoint: {
+ required: !!prometheusToggleEnabled,
+ validator: { error: 'endpoint is required', regex: /^.*$/ },
+ },
+ },
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ onValidation,
+ )
+
const setPrometheusToggle = () => {
setPrometheusToggleEnabled(!prometheusToggleEnabled)
}
const OnPrometheusAuthTypeChange = (e) => {
handleOnChange(e)
- if (state.authType.value == AuthenticationType.BASIC) {
+ if (state.authType.value === AuthenticationType.BASIC) {
setPrometheusAuthenticationType({ type: AuthenticationType.ANONYMOUS })
} else {
setPrometheusAuthenticationType({ type: AuthenticationType.BASIC })
}
}
- const ClusterInfoComponent = () => {
+ const renderClusterInfo = () => {
const k8sClusters = Object.values(CLUSTER_COMMAND)
return (
<>
@@ -601,18 +606,16 @@ export default function ClusterForm({
)
}
- const clusterLabel = () => {
- return (
-
- Server URL & Bearer token{isDefaultCluster() ? '' : '*'}
-
-
-
- How to find for
-
-
- )
- }
+ const clusterLabel = () => (
+
+ Server URL & Bearer token{isDefaultCluster ? '' : '*'}
+
+
+
+ How to find for
+ {renderClusterInfo()}
+
+ )
const onFileChange = (e): void => {
setUploadState(UPLOAD_STATE.UPLOADING)
@@ -621,7 +624,9 @@ export default function ClusterForm({
reader.onload = () => {
try {
setSaveYamlData(reader.result.toString())
- } catch (e) {}
+ } catch {
+ noop()
+ }
}
reader.readAsText(file)
setUploadState(UPLOAD_STATE.SUCCESS)
@@ -641,9 +646,6 @@ export default function ClusterForm({
if (isKubeConfigFile) {
toggleKubeConfigFile(!isKubeConfigFile)
}
- if (getClusterVar) {
- toggleGetCluster()
- }
if (isClusterDetails) {
toggleClusterDetails(!isClusterDetails)
}
@@ -671,11 +673,34 @@ export default function ClusterForm({
setSSHConnectionType(authType)
}
- const setRemoteConnectionFalse = () => {
- setIsConnectedViaProxyTemp(false)
- setIsConnectedViaSSHTunnelTemp(false)
+ const clusterTitle = () => {
+ if (!id) {
+ return 'Add Cluster'
+ }
+ return 'Edit Cluster'
}
+ const renderHeader = () => (
+
+
+ {clusterTitle()}
+
+
+ }
+ dataTestId="header_close_icon"
+ component={ButtonComponentType.button}
+ style={ButtonStyleType.negativeGrey}
+ size={ComponentSizeType.xs}
+ variant={ButtonVariantType.borderLess}
+ ariaLabel="Close edit cluster drawer"
+ disabled={loader}
+ onClick={handleCloseButton}
+ showAriaLabelInTippy={false}
+ />
+
+ )
+
const renderUrlAndBearerToken = () => {
let proxyConfig
let sshConfig
@@ -701,13 +726,29 @@ export default function ClusterForm({
}
}
const passedRemoteConnectionMethod = { value: remoteConnectionMethod, error: '' }
+
+ const getTokenText = () => {
+ if (!id) {
+ return state.token.value
+ }
+
+ return id === DEFAULT_CLUSTER_ID ? '' : DEFAULT_SECRET_PLACEHOLDER
+ }
+
+ const getGrafanaModuleSectionClassName = () => {
+ if (prometheusToggleEnabled) {
+ return 'mb-20'
+ }
+ return prometheusUrl ? 'mb-20' : 'mb-40'
+ }
+
return (
<>
@@ -730,15 +771,7 @@ export default function ClusterForm({
{id !== DEFAULT_CLUSTER_ID && (
)}
{isGrafanaModuleInstalled && (
-
+
See metrics for applications in this cluster
@@ -865,7 +898,7 @@ export default function ClusterForm({
)}
- {isGrafanaModuleInstalled && !prometheusToggleEnabled && prometheus_url && }
+ {isGrafanaModuleInstalled && !prometheusToggleEnabled && prometheusUrl && }
{isGrafanaModuleInstalled && prometheusToggleEnabled && (
{(state.userName.error || state.password.error || state.endpoint.error) && (
@@ -951,136 +984,72 @@ export default function ClusterForm({
setSaveYamlData(val)
}
- const codeEditor = () => {
- return (
-
-
-
-
- Paste the contents of kubeconfig file here
-
- {uploadState !== UPLOAD_STATE.UPLOADING && (
-
- Browse file...
-
- )}
-
-
-
-
- {hasValidationError && }
-
-
- )
- }
-
- const LoadingCluster = (): JSX.Element => {
- return (
-
-
- Add Cluster
-
-
-
-
-
-
-
- )
- }
-
- const NoMatchingResults = (): JSX.Element => {
- return
- }
-
- if (loader) {
- return
- }
-
- const clusterTitle = () => {
- if (!id) {
- return 'Add Cluster'
- }
- return 'Edit Cluster'
- }
-
- const AddClusterHeader = () => (
-
-
- {clusterTitle()}
-
-
-
+
+
+ Paste the contents of kubeconfig file here
+
+ {uploadState !== UPLOAD_STATE.UPLOADING && (
+
+ Browse file...
+
+ )}
+
+
+
+
+ {hasValidationError && }
+
+
)
const saveClusterDetails = (): JSX.Element => (
-
-
+ <>
-
CLUSTER
STATUS
MESSAGE
-
{!saveClusterList || saveClusterList.length === 0 ? (
-
+
) : (
saveClusterList.map((clusterListDetail, index) => (
-
-
-
-
-
-
+
+ {id && (
+
+
+
+
+ )}
+
+ {!id && (
+
+
+
+
+
+ )}
+ >
)
const handleClusterDetailCall = async () => {
@@ -1137,10 +1127,10 @@ export default function ClusterForm({
toggleClusterDetails(true)
}
- function toggleIsSelected(clusterName: string, forceUnselect?: boolean) {
+ const toggleIsSelected = (clusterNameString: string, forceUnselect?: boolean) => {
const _currentSelections = {
...isClusterSelected,
- [clusterName]: forceUnselect ? false : !isClusterSelected[clusterName],
+ [clusterNameString]: forceUnselect ? false : !isClusterSelected[clusterNameString],
}
setClusterSeleceted(_currentSelections)
@@ -1151,7 +1141,7 @@ export default function ClusterForm({
}
}
- function toggleSelectAll(event) {
+ const toggleSelectAll = (event) => {
if (isCheckboxDisabled()) {
return
}
@@ -1173,7 +1163,7 @@ export default function ClusterForm({
setClusterSeleceted(currentSelections)
}
- function validCluster() {
+ const validCluster = () => {
const _validCluster = dataList
let count = 0
@@ -1188,7 +1178,7 @@ export default function ClusterForm({
}
})
if (found) {
- count++
+ count += 1
}
})
return count
@@ -1201,6 +1191,7 @@ export default function ClusterForm({
if (Object.values(isClusterSelected).some((_selected) => _selected)) {
return CHECKBOX_VALUE.INTERMEDIATE
}
+ return null
}
const onChangeUserName = (selectedOption: any, clusterDetail: DataListType) => {
@@ -1211,23 +1202,14 @@ export default function ClusterForm({
toggleIsSelected(clusterDetail.cluster_name, true)
}
- if (loader) {
- return
- }
-
const displayClusterDetails = () => {
const isAnyCheckboxSelected = Object.values(isClusterSelected).some((value) => value === true)
return (
<>
{isKubeConfigFile && (
-
-
-
+
-
+
@@ -1239,7 +1221,7 @@ export default function ClusterForm({
Icon={InfoIcon}
styles={{ borderRadius: '3px 3px 0 0' }}
/>
-
+
- {!dataList || dataList.length === 0 ? (
-
+ {!dataList?.length ? (
+
) : (
dataList.map((clusterDetail, index) => (
0
+ ?.errorInConnecting.length > 0
}
/>
{
if (
selectedUserNameOptions[clusterDetail.cluster_name]
- .errorInConnecting !== 'cluster-already-exists' &&
+ ?.errorInConnecting !== 'cluster-already-exists' &&
selectedUserNameOptions[clusterDetail.cluster_name]
- .errorInConnecting.length > 0
+ ?.errorInConnecting.length > 0
) {
return
}
@@ -1331,9 +1312,9 @@ export default function ClusterForm({
)}
{selectedUserNameOptions[clusterDetail.cluster_name]
- .errorInConnecting !== '' &&
+ ?.errorInConnecting !== '' &&
selectedUserNameOptions[clusterDetail.cluster_name]
- .errorInConnecting !== 'cluster-already-exists' && (
+ ?.errorInConnecting !== 'cluster-already-exists' && (
{selectedUserNameOptions[clusterDetail.cluster_name]
- .errorInConnecting.length !== 0 && (
+ ?.errorInConnecting.length !== 0 && (
@@ -1361,31 +1342,60 @@ export default function ClusterForm({
-
-
- handleClusterDetailCall()}
- disabled={!saveClusterList || !isAnyCheckboxSelected}
- >
- Save
-
-
+
+
+ Edit Kubeconfig
+
+
+ handleClusterDetailCall()}
+ disabled={!saveClusterList || !isAnyCheckboxSelected}
+ >
+ Save
+
+
+ )}
+
+
+
+
+
+
+ Edit Kubeconfig
+
+
+
+
+ handleClusterDetailCall()}
+ disabled={!saveClusterList || !isAnyCheckboxSelected}
+ >
+ Save
+
+
+
)}
{isClusterDetails && !isKubeConfigFile && saveClusterDetails()}
@@ -1393,45 +1403,11 @@ export default function ClusterForm({
)
}
- const handleVirtualCloseButton = (e) => {
- toggleEditMode(e)
- setLoadingState(false)
- reload()
- handleCloseCreateClusterForm()
- }
-
const showConfirmationModal = () => setConfirmation(true)
const hideConfirmationModal = () => setConfirmation(false)
- const onDelete = async () => {
- const payload = {
- id,
- cluster_name,
- config: { bearer_token: state.token.value },
- active,
- prometheus_url: prometheusToggleEnabled ? state.endpoint.value : '',
- prometheusAuth: {
- userName: prometheusToggleEnabled ? state.userName.value : '',
- password: prometheusToggleEnabled ? state.password.value : '',
- tlsClientCert: prometheusToggleEnabled ? state.prometheusTlsClientKey.value : '',
- tlsClientKey: prometheusToggleEnabled ? state.prometheusTlsClientCert.value : '',
- },
- remoteConnectionConfig: getRemoteConnectionConfig(state, remoteConnectionMethod),
- server_url,
- defaultClusterComponent,
- k8sversion: '',
- insecureSkipTlsVerify: !isTlsConnection,
- }
- await deleteCluster(payload)
- reload()
- }
-
- const renderNonVirtualFooter = () => {
- if (isVirtual) {
- return null
- }
-
- if (isKubeConfigFile) {
+ const renderFooter = () => {
+ if (isKubeConfigFile && id) {
return (
+
+ }
+ disabled={isDefaultCluster}
+ dataTestId="get_cluster_button"
+ onClick={handleGetClustersClick}
+ isLoading={loader}
+ />
+
+
+ )
+ }
+
return (
-
+
{id && (
}
- disabled={isDefaultCluster()}
+ disabled={isDefaultCluster || loader}
dataTestId="delete_cluster"
onClick={showConfirmationModal}
/>
)}
-
-
-
-
+ {id ? (
+
+
+
+
+ ) : (
+
+
+
+
+
+ )}
)
}
- return getClusterVar ? (
- displayClusterDetails()
- ) : (
-
-
-
- {VirtualClusterSelectionTab && (
-
- )}
- {!isVirtual && (
- <>
+ return (
+
+ {id && renderHeader()}
+
+ {dataList.length ? (
+ displayClusterDetails()
+ ) : (
+ <>
+
{!id && (
-
+
{confirmation && (
-
)}
- >
- )}
-
+
- {renderNonVirtualFooter()}
+ {renderFooter()}
+ >
+ )}
)
}
+
+export default ClusterForm
diff --git a/src/components/cluster/DeleteClusterConfirmationModal.tsx b/src/components/cluster/DeleteClusterConfirmationModal.tsx
new file mode 100644
index 0000000000..8b0a4fdb18
--- /dev/null
+++ b/src/components/cluster/DeleteClusterConfirmationModal.tsx
@@ -0,0 +1,65 @@
+import { useState } from 'react'
+
+import {
+ Checkbox,
+ CHECKBOX_VALUE,
+ DC_DELETE_SUBTITLES,
+ DeleteConfirmationModal,
+ ERROR_STATUS_CODE,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { importComponentFromFELibrary } from '@Components/common'
+
+import { DeleteComponentsName } from '../../config/constantMessaging'
+import { deleteCluster } from './cluster.service'
+import { DeleteClusterConfirmationModalProps } from './cluster.type'
+
+const deleteInstalledCluster = importComponentFromFELibrary('deleteInstalledCluster', null, 'function')
+
+const DeleteClusterConfirmationModal = ({
+ clusterName,
+ clusterId,
+ handleClose,
+ installationId,
+ reload,
+}: DeleteClusterConfirmationModalProps) => {
+ const [shouldDeleteInstalledCluster, setShouldDeleteInstalledCluster] = useState(false)
+
+ const handleDelete = async () => {
+ if (shouldDeleteInstalledCluster && deleteInstalledCluster) {
+ await deleteInstalledCluster(Number(installationId))
+ }
+ await deleteCluster({ id: Number(clusterId) })
+ reload()
+ }
+
+ const handleToggleShouldDeleteInstalledCluster = () => {
+ setShouldDeleteInstalledCluster(!shouldDeleteInstalledCluster)
+ }
+
+ return (
+
+ {!!installationId && !!deleteInstalledCluster && (
+
+ Delete cluster from AWS
+
+ )}
+
+ )
+}
+
+export default DeleteClusterConfirmationModal
diff --git a/src/components/cluster/cluster.scss b/src/components/cluster/cluster.scss
index 5088bd4533..08e959b9e2 100644
--- a/src/components/cluster/cluster.scss
+++ b/src/components/cluster/cluster.scss
@@ -70,13 +70,13 @@
.saved-cluster-list-row {
display: grid;
- grid-template-columns: 0px minmax(278px, 346px) 100px 360px 190px minmax(80px, 90px);
+ grid-template-columns: minmax(278px, 346px) 100px 1fr;
grid-column-gap: 16px;
}
.cluster-list-row-1 {
display: grid;
- grid-template-columns: 24px minmax(240px, 250px) 150px 420px minmax(80px, 90px);
+ grid-template-columns: 24px minmax(240px, 250px) 150px 1fr;
grid-column-gap: 16px;
line-height: 20px;
}
@@ -85,4 +85,8 @@
.react-monaco-editor-container {
flex-grow: 1;
}
+
+ .radio__button {
+ margin-top: 2px;
+ }
}
diff --git a/src/components/cluster/cluster.service.ts b/src/components/cluster/cluster.service.ts
index e9168a417c..57737beac1 100644
--- a/src/components/cluster/cluster.service.ts
+++ b/src/components/cluster/cluster.service.ts
@@ -16,6 +16,7 @@
import { get, post, put, trash, ResponseType } from '@devtron-labs/devtron-fe-common-lib'
import { Routes } from '../../config'
+import { DeleteClusterPayload } from './cluster.type'
export function getClusterList(): Promise {
const URL = `${Routes.CLUSTER}`
@@ -67,8 +68,8 @@ export const getEnvironmentList = (): Promise => {
return get(URL).then((response) => response)
}
-export function deleteCluster(request): Promise {
- return trash(Routes.CLUSTER, request)
+export function deleteCluster(payload: DeleteClusterPayload): Promise {
+ return trash(Routes.CLUSTER, payload)
}
export function deleteEnvironment(request): Promise {
diff --git a/src/components/cluster/cluster.type.ts b/src/components/cluster/cluster.type.ts
index 535dfb8f90..3c669619df 100644
--- a/src/components/cluster/cluster.type.ts
+++ b/src/components/cluster/cluster.type.ts
@@ -179,45 +179,32 @@ export interface ClusterFormType {
export const RemoteConnectionTypeCluster = 'cluster'
-export type ClusterFormProps = {
- /**
- * @default false
- */
+export type EditClusterFormProps = {
+ id: number
+ toggleEditMode: Dispatch>
isProd?: boolean
- cluster_name: string
- server_url: string
- active: boolean
- config: any
- reload: () => void
- prometheus_url: string
+ clusterName: string
+ serverUrl: string
+ prometheusUrl: string
prometheusAuth: any
- defaultClusterComponent: any
proxyUrl: string
sshUsername: string
sshPassword: string
sshAuthKey: string
sshServerAddress: string
- isConnectedViaProxy: boolean
isConnectedViaSSHTunnel: boolean
isTlsConnection: boolean
- toggleCheckTlsConnection: any
- setTlsConnectionFalse: any
- toggleKubeConfigFile: any
- isKubeConfigFile: boolean
- isClusterDetails: boolean
- toggleClusterDetails: any
- isVirtualCluster: boolean
-} & (
- | {
- id: number
- toggleEditMode: Dispatch>
+}
+
+export type ClusterFormProps = { reload: () => void } & (
+ | ({
handleCloseCreateClusterForm?: never
- }
- | {
+ id: number
+ } & EditClusterFormProps)
+ | ({
handleCloseCreateClusterForm: () => void
id?: never
- toggleEditMode?: never
- }
+ } & Partial>)
)
export interface AddClusterFormPrefilledInfoType {
@@ -227,3 +214,15 @@ export interface AddClusterFormPrefilledInfoType {
export interface AddEnvironmentFormPrefilledInfoType {
namespace: string
}
+
+export interface DeleteClusterConfirmationModalProps {
+ clusterId: string
+ clusterName: string
+ handleClose: () => void
+ installationId?: string
+ reload?: () => void
+}
+
+export interface DeleteClusterPayload {
+ id: number
+}
diff --git a/src/components/v2/appDetails/sourceInfo/EnvironmentSelector.component.tsx b/src/components/v2/appDetails/sourceInfo/EnvironmentSelector.component.tsx
index 2d71d0880b..e4077ac0f3 100644
--- a/src/components/v2/appDetails/sourceInfo/EnvironmentSelector.component.tsx
+++ b/src/components/v2/appDetails/sourceInfo/EnvironmentSelector.component.tsx
@@ -40,7 +40,7 @@ import { ReactComponent as LinkIcon } from '../../../../assets/icons/ic-link.svg
import { ReactComponent as Trash } from '../../../../assets/icons/ic-delete-interactive.svg'
import { deleteApplicationRelease } from '../../../external-apps/ExternalAppService'
import { deleteInstalledChart } from '../../../charts/charts.service'
-import { ReactComponent as Dots } from '../../assets/icons/ic-menu-dot.svg'
+import { ReactComponent as Dots } from '@Icons/ic-dot.svg'
import { DELETE_ACTION, URLS, checkIfDevtronOperatorHelmRelease } from '../../../../config'
import { ReactComponent as BinWithDots } from '../../../../assets/icons/ic-delete-dots.svg'
import { DELETE_DEPLOYMENT_PIPELINE, DeploymentAppTypeNameMapping } from '../../../../config/constantMessaging'
@@ -384,7 +384,7 @@ const EnvironmentSelectorComponent = ({
>
-
+
diff --git a/src/components/v2/assets/icons/ic-menu-dot.svg b/src/components/v2/assets/icons/ic-menu-dot.svg
deleted file mode 100644
index 45b997a551..0000000000
--- a/src/components/v2/assets/icons/ic-menu-dot.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
diff --git a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx
index 0ea4ae39a6..b92845976e 100644
--- a/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx
+++ b/src/components/v2/values/chartValuesDiff/ChartValuesView.component.tsx
@@ -644,7 +644,7 @@ export const ActiveReadmeColumn = ({ fetchingReadMe, activeReadMe }: ActiveReadm
return (
Readme
diff --git a/src/config/constants.ts b/src/config/constants.ts
index 533d756406..53d5d9b87f 100644
--- a/src/config/constants.ts
+++ b/src/config/constants.ts
@@ -150,7 +150,6 @@ export const Routes = {
CHART_INSTALLED: 'app-store/installed-app',
ARGO_APPS: 'argo-application',
FLUX_APPS: 'flux-application',
- CHART_AVAILABLE: 'app-store',
CHART_STORE: 'app-store',
CHART_REPO: 'chart-repo',
CHART_RESYNC: 'sync-charts',
@@ -390,6 +389,7 @@ export const DOCUMENTATION = {
SECURITY: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/security-features`,
SPECIFY_IMAGE_PULL_SECRET: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/getting-started/global-configurations/container-registries#specify-image-pull-secret`,
WEBHOOK_CI: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/usage/applications/creating-application/ci-pipeline#3.-deploy-image-from-external-service`,
+ CLUSTER_AND_ENVIRONMENT: `${DOCUMENTATION_HOME_PAGE}${DOCUMENTATION_VERSION}/global-configurations/cluster-and-environments`,
}
export const HEADER_TEXT = {
diff --git a/src/config/routes.ts b/src/config/routes.ts
index 316a3a50c5..896424395f 100644
--- a/src/config/routes.ts
+++ b/src/config/routes.ts
@@ -24,6 +24,8 @@ export interface NavItem {
isLocked: boolean
}
+const CREATE_CLUSTER_PATH = 'create/cluster/:type(connect-cluster|create-eks-cluster|add-isolated-cluster)'
+
export const URLS = {
CHARTS: '/chart-store',
CHARTS_DISCOVER: '/chart-store/discover',
@@ -32,6 +34,8 @@ export const URLS = {
CREATE_JOB: 'create-job',
APPLICATION_GROUP: '/application-group',
RESOURCE_BROWSER: COMMON_URLS.RESOURCE_BROWSER,
+ RESOURCE_BROWSER_INSTALLATION_CLUSTER: `${COMMON_URLS.RESOURCE_BROWSER}/installation-cluster/:installationId`,
+ RESOURCE_BROWSER_CREATE_CLUSTER: `${COMMON_URLS.RESOURCE_BROWSER}/${CREATE_CLUSTER_PATH}`,
EXTERNAL_APPS: 'ea',
DEVTRON_CHARTS: 'dc',
EXTERNAL_ARGO_APP: 'eaa',
@@ -83,7 +87,7 @@ export const URLS = {
GLOBAL_CONFIG_GITOPS: '/global-config/gitops',
GLOBAL_CONFIG_DOCKER: '/global-config/docker',
GLOBAL_CONFIG_CLUSTER: '/global-config/cluster-env',
- GLOBAL_CONFIG_CREATE_CLUSTER: '/global-config/cluster-env/create/cluster',
+ GLOBAL_CONFIG_CREATE_CLUSTER: `/global-config/cluster-env/${CREATE_CLUSTER_PATH}`,
GLOBAL_CONFIG_CHART: '/global-config/chart-repo',
GLOBAL_CONFIG_AUTH: '/global-config/auth',
GLOBAL_CONFIG_AUTH_USER_PERMISSION: '/global-config/auth/users',
diff --git a/src/css/base.scss b/src/css/base.scss
index 4d5ddbcd4a..42299caeaa 100644
--- a/src/css/base.scss
+++ b/src/css/base.scss
@@ -2802,6 +2802,10 @@ button.anchor {
min-width: 152px;
}
+.mw-232 {
+ min-width: 232px;
+}
+
.mw-264 {
min-width: 264px;
}
diff --git a/src/services/service.ts b/src/services/service.ts
index 60c1028e69..50fc0d9ef4 100644
--- a/src/services/service.ts
+++ b/src/services/service.ts
@@ -174,7 +174,7 @@ export function getAvailableCharts(
pageSize?: number,
options?: APIOptions,
): Promise<{ code: number; result: Chart[] }> {
- let url = `${Routes.CHART_AVAILABLE}/discover/`
+ let url = `${Routes.CHART_STORE}/discover/`
if (pageOffset >= 0 && pageSize) {
queryString = `${queryString || '?'}&offset=${pageOffset}&size=${pageSize}`
|