github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/settings/components/cluster-details/cluster-details.tsx (about)

     1  import * as classNames from 'classnames';
     2  import * as moment from 'moment';
     3  import * as React from 'react';
     4  import {FieldApi, FormField as ReactFormField, Text} from 'react-form';
     5  import {RouteComponentProps, Link} from 'react-router-dom';
     6  import {from, timer} from 'rxjs';
     7  import {mergeMap} from 'rxjs/operators';
     8  
     9  import {FormField, Ticker} from 'argo-ui';
    10  import {ConnectionStateIcon, DataLoader, EditablePanel, Page, Timestamp, MapInputField} from '../../../shared/components';
    11  import {Cluster} from '../../../shared/models';
    12  import {services} from '../../../shared/services';
    13  import {formatClusterQueryParam} from '../../../shared/utils';
    14  
    15  function isRefreshRequested(cluster: Cluster): boolean {
    16      return cluster.info.connectionState.attemptedAt && cluster.refreshRequestedAt && moment(cluster.info.connectionState.attemptedAt).isBefore(moment(cluster.refreshRequestedAt));
    17  }
    18  
    19  export const NamespacesEditor = ReactFormField((props: {fieldApi: FieldApi; className: string}) => {
    20      const val = (props.fieldApi.getValue() || []).join(',');
    21      return <input className={props.className} value={val} onChange={event => props.fieldApi.setValue(event.target.value.split(','))} />;
    22  });
    23  
    24  export const ClusterDetails = (props: RouteComponentProps<{server: string}>) => {
    25      const server = decodeURIComponent(props.match.params.server);
    26      const loaderRef = React.useRef<DataLoader>();
    27      const [updating, setUpdating] = React.useState(false);
    28      return (
    29          <DataLoader ref={loaderRef} input={server} load={(url: string) => timer(0, 1000).pipe(mergeMap(() => from(services.clusters.get(url, ''))))}>
    30              {(cluster: Cluster) => (
    31                  <Page
    32                      title='Clusters'
    33                      toolbar={{
    34                          breadcrumbs: [{title: 'Settings', path: '/settings'}, {title: 'Clusters', path: '/settings/clusters'}, {title: server}],
    35                          actionMenu: {
    36                              items: [
    37                                  {
    38                                      iconClassName: classNames('fa fa-redo', {'status-icon--spin': isRefreshRequested(cluster)}),
    39                                      title: 'Invalidate Cache',
    40                                      disabled: isRefreshRequested(cluster) || updating,
    41                                      action: async () => {
    42                                          setUpdating(true);
    43                                          try {
    44                                              const updated = await services.clusters.invalidateCache(props.match.params.server);
    45                                              loaderRef.current.setData(updated);
    46                                          } finally {
    47                                              setUpdating(false);
    48                                          }
    49                                      }
    50                                  }
    51                              ]
    52                          }
    53                      }}>
    54                      <p />
    55  
    56                      <div className='argo-container'>
    57                          <EditablePanel
    58                              values={cluster}
    59                              save={async updated => {
    60                                  const item = await services.clusters.get(updated.server, '');
    61                                  item.name = updated.name;
    62                                  item.namespaces = updated.namespaces;
    63                                  item.labels = updated.labels;
    64                                  item.annotations = updated.annotations;
    65                                  loaderRef.current.setData(await services.clusters.update(item, 'name', 'namespaces', 'labels', 'annotations'));
    66                              }}
    67                              title='GENERAL'
    68                              items={[
    69                                  {
    70                                      title: 'SERVER',
    71                                      view: cluster.server
    72                                  },
    73                                  {
    74                                      title: 'CREDENTIALS TYPE',
    75                                      view:
    76                                          (cluster.config.awsAuthConfig && `IAM AUTH (cluster name: ${cluster.config.awsAuthConfig.clusterName})`) ||
    77                                          (cluster.config.execProviderConfig && `External provider (command: ${cluster.config.execProviderConfig.command})`) ||
    78                                          'Token/Basic Auth'
    79                                  },
    80                                  {
    81                                      title: 'NAME',
    82                                      view: cluster.name,
    83                                      edit: formApi => <FormField formApi={formApi} field='name' component={Text} />
    84                                  },
    85                                  {
    86                                      title: 'NAMESPACES',
    87                                      view: ((cluster.namespaces || []).length === 0 && 'All namespaces') || cluster.namespaces.join(', '),
    88                                      edit: formApi => <FormField formApi={formApi} field='namespaces' component={NamespacesEditor} />
    89                                  },
    90                                  {
    91                                      title: 'APPLICATIONS',
    92                                      view: (
    93                                          <div>
    94                                              <DataLoader load={() => services.applications.list([])}>
    95                                                  {apps => (
    96                                                      <Link to={`/applications?cluster=${formatClusterQueryParam(cluster)}`}>
    97                                                          {
    98                                                              apps.items.filter(app => app.spec.destination.name === cluster.name || app.spec.destination.server === cluster.server)
    99                                                                  .length
   100                                                          }
   101                                                      </Link>
   102                                                  )}
   103                                              </DataLoader>
   104                                          </div>
   105                                      )
   106                                  },
   107                                  {
   108                                      title: 'LABELS',
   109                                      view: Object.keys(cluster.labels || [])
   110                                          .map(label => `${label}=${cluster.labels[label]}`)
   111                                          .join(' '),
   112                                      edit: formApi => <FormField formApi={formApi} field='labels' component={MapInputField} />
   113                                  },
   114                                  {
   115                                      title: 'ANNOTATIONS',
   116                                      view: Object.keys(cluster.annotations || [])
   117                                          .map(annotation => `${annotation}=${cluster.annotations[annotation]}`)
   118                                          .join(' '),
   119                                      edit: formApi => <FormField formApi={formApi} field='annotations' component={MapInputField} />
   120                                  }
   121                              ]}
   122                          />
   123                          <div className='white-box'>
   124                              <p>CONNECTION STATE</p>
   125                              <div className='white-box__details'>
   126                                  <div className='row white-box__details-row'>
   127                                      <div className='columns small-3'>STATUS:</div>
   128                                      <div className='columns small-9'>
   129                                          <ConnectionStateIcon state={cluster.info.connectionState} /> {cluster.info.connectionState.status}
   130                                      </div>
   131                                  </div>
   132                                  <div className='row white-box__details-row'>
   133                                      <div className='columns small-3'>VERSION:</div>
   134                                      <div className='columns small-9'> {cluster.info.serverVersion}</div>
   135                                  </div>
   136                                  <div className='row white-box__details-row'>
   137                                      <div className='columns small-3'>DETAILS:</div>
   138                                      <div className='columns small-9'> {cluster.info.connectionState.message} </div>
   139                                  </div>
   140                                  <div className='row white-box__details-row'>
   141                                      <div className='columns small-3'>MODIFIED AT:</div>
   142                                      <div className='columns small-9'>
   143                                          <Ticker>
   144                                              {now => {
   145                                                  if (!cluster.info.connectionState.attemptedAt) {
   146                                                      return <span>Never (next refresh in few seconds)</span>;
   147                                                  }
   148                                                  const secondsBeforeRefresh = Math.round(
   149                                                      Math.max(10 - moment(now).diff(moment(cluster.info.connectionState.attemptedAt)) / 1000, 1)
   150                                                  );
   151                                                  return (
   152                                                      <React.Fragment>
   153                                                          <Timestamp date={cluster.info.connectionState.attemptedAt} /> (next refresh in {secondsBeforeRefresh} seconds)
   154                                                      </React.Fragment>
   155                                                  );
   156                                              }}
   157                                          </Ticker>
   158                                      </div>
   159                                  </div>
   160                              </div>
   161                          </div>
   162  
   163                          <div className='white-box'>
   164                              <p>CACHE INFO</p>
   165                              <div className='white-box__details'>
   166                                  <Ticker>
   167                                      {() => (
   168                                          <div className='row white-box__details-row'>
   169                                              <div className='columns small-3'>RE-SYNCHRONIZED:</div>
   170                                              <div className='columns small-9'>
   171                                                  <Timestamp date={cluster.info.cacheInfo.lastCacheSyncTime} />
   172                                              </div>
   173                                          </div>
   174                                      )}
   175                                  </Ticker>
   176                                  <div className='row white-box__details-row'>
   177                                      <div className='columns small-3'>APIs COUNT:</div>
   178                                      <div className='columns small-9'> {cluster.info.cacheInfo.apisCount} </div>
   179                                  </div>
   180                                  <div className='row white-box__details-row'>
   181                                      <div className='columns small-3'>RESOURCES COUNT:</div>
   182                                      <div className='columns small-9'> {cluster.info.cacheInfo.resourcesCount} </div>
   183                                  </div>
   184                                  <div className='row white-box__details-row'>
   185                                      <div className='columns small-3'>APPLICATIONS COUNT:</div>
   186                                      <div className='columns small-9'> {cluster.info.applicationsCount} </div>
   187                                  </div>
   188                              </div>
   189                          </div>
   190                      </div>
   191                  </Page>
   192              )}
   193          </DataLoader>
   194      );
   195  };