Skip to content

Commit

Permalink
Update metric used for cells (#4009)
Browse files Browse the repository at this point in the history
- `firehose_value_metric_rep_unhealthy_cell` deprecated, now use `firehose_value_metric_rep_garden_health_check_failed`
- Try new metric first and fall back on old
  • Loading branch information
richard-cox authored and nwmac committed Nov 7, 2019
1 parent c6a507e commit 173df4b
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 116 deletions.
55 changes: 5 additions & 50 deletions src/frontend/packages/core/src/core/endpoints.service.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Store } from '@ngrx/store';
import { combineLatest as observableCombineLatest, Observable, of } from 'rxjs';
import { filter, first, map, skipWhile, switchMap, withLatestFrom } from 'rxjs/operators';
import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { first, map, skipWhile, withLatestFrom } from 'rxjs/operators';

import { FetchCFCellMetricsPaginatedAction, MetricQueryConfig } from '../../../store/src/actions/metrics.actions';
import { RouterNav } from '../../../store/src/actions/router.actions';
import { AppState, IRequestEntityTypeState } from '../../../store/src/app-state';
import { entityFactory } from '../../../store/src/helpers/entity-factory';
import { AuthState } from '../../../store/src/reducers/auth.reducer';
import { getPaginationObservables } from '../../../store/src/reducers/pagination-reducer/pagination-reducer.helper';
import {
endpointEntitiesSelector,
endpointsEntityRequestDataSelector,
endpointStatusSelector,
} from '../../../store/src/selectors/endpoint.selectors';
import { IMetrics } from '../../../store/src/types/base-metric.types';
import { endpointEntitiesSelector, endpointStatusSelector } from '../../../store/src/selectors/endpoint.selectors';
import { EndpointModel, EndpointState } from '../../../store/src/types/endpoint.types';
import { EndpointHealthCheck, endpointHealthChecks } from '../../endpoints-health-checks';
import { getEndpointType } from '../features/endpoints/endpoint-helpers';
import { PaginationMonitorFactory } from '../shared/monitors/pagination-monitor.factory';
import { MetricQueryType } from '../shared/services/metrics-range-selector.types';
import { endpointHasMetricsByAvailable, getEndpointType } from '../features/endpoints/endpoint-helpers';
import { UserService } from './user.service';


Expand All @@ -47,7 +37,6 @@ export class EndpointsService implements CanActivate {
constructor(
private store: Store<AppState>,
private userService: UserService,
private paginationMonitorFactory: PaginationMonitorFactory
) {
this.endpoints$ = store.select(endpointEntitiesSelector);
this.haveRegistered$ = this.endpoints$.pipe(map(endpoints => !!Object.keys(endpoints).length));
Expand Down Expand Up @@ -122,41 +111,7 @@ export class EndpointsService implements CanActivate {
}

hasMetrics(endpointId: string): Observable<boolean> {
return this.store.select(endpointsEntityRequestDataSelector(endpointId)).pipe(
filter(endpoint => !!endpoint),
map(endpoint => endpoint.metricsAvailable),
first()
);
}

hasCellMetrics(endpointId: string): Observable<boolean> {
return this.hasMetrics(endpointId).pipe(
switchMap(hasMetrics => {
if (!hasMetrics) {
return of(false);
}

// Check that we successfully retrieve some stats. If the metric is unknown an empty list is returned
const action = new FetchCFCellMetricsPaginatedAction(
endpointId,
endpointId,
new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}),
MetricQueryType.QUERY
);
return getPaginationObservables<IMetrics>({
store: this.store,
action,
paginationMonitor: this.paginationMonitorFactory.create(
action.paginationKey,
entityFactory(action.entityKey)
)
}).entities$.pipe(
filter(entities => !!entities && !!entities.length),
map(entities => !!entities.find(entity => !!entity.data.result.length)),
first()
);
})
);
return endpointHasMetricsByAvailable(this.store, endpointId);
}

doesNotHaveConnectedEndpointType(type: string): Observable<boolean> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Store } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { filter, first, map, publishReplay, refCount, switchMap } from 'rxjs/operators';

import { FetchCFCellMetricsPaginatedAction, MetricQueryConfig } from '../../../../store/src/actions/metrics.actions';
import { AppState } from '../../../../store/src/app-state';
import { entityFactory } from '../../../../store/src/helpers/entity-factory';
import { getPaginationObservables } from '../../../../store/src/reducers/pagination-reducer/pagination-reducer.helper';
import { IMetrics } from '../../../../store/src/types/base-metric.types';
import { PaginationMonitorFactory } from '../../shared/monitors/pagination-monitor.factory';
import { MetricQueryType } from '../../shared/services/metrics-range-selector.types';
import { endpointHasMetricsByAvailable } from '../endpoints/endpoint-helpers';
import { CellMetrics } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service';

export class CfCellHelper {

constructor(
private store: Store<AppState>,
private paginationMonitorFactory: PaginationMonitorFactory) {
}

public createCellMetricAction(cfId: string, cellId?: string): Observable<FetchCFCellMetricsPaginatedAction> {
const cellIdString = !!cellId ? `{bosh_job_id="${cellId}"}` : '';

const newMetricAction: FetchCFCellMetricsPaginatedAction = new FetchCFCellMetricsPaginatedAction(
cfId,
cfId,
new MetricQueryConfig(CellMetrics.HEALTHY + cellIdString, {}),
MetricQueryType.QUERY
);
return this.hasMetric(newMetricAction).pipe(
switchMap(hasNewMetric => hasNewMetric ?
of(hasNewMetric) :
this.hasMetric(new FetchCFCellMetricsPaginatedAction(
cfId,
cfId,
new MetricQueryConfig(CellMetrics.HEALTHY_DEP + cellIdString, {}),
MetricQueryType.QUERY
))
)
);
}

private hasMetric(action: FetchCFCellMetricsPaginatedAction): Observable<FetchCFCellMetricsPaginatedAction> {
return getPaginationObservables<IMetrics>({
store: this.store,
action,
paginationMonitor: this.paginationMonitorFactory.create(
action.paginationKey,
entityFactory(action.entityKey)
)
}).entities$.pipe(
filter(entities => !!entities && !!entities.length),
first(),
map(entities => !!entities.find(entity => !!entity.data.result.length) ? action : null),
publishReplay(1),
refCount()
);
}

public hasCellMetrics(endpointId: string): Observable<boolean> {
return endpointHasMetricsByAvailable(this.store, endpointId).pipe(
// If metrics set up for this endpoint check if we can fetch cell metrics from it.
// If the metric is unknown an empty list is returned
switchMap(hasMetrics => hasMetrics ? this.createCellMetricAction(endpointId).pipe(map(action => !!action)) : of(false))
);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { map, switchMap } from 'rxjs/operators';

import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../../../store/src/actions/metrics.actions';
import { AppState } from '../../../../../../../store/src/app-state';
import { entityFactory, metricSchemaKey } from '../../../../../../../store/src/helpers/entity-factory';
import { IMetricMatrixResult, IMetrics, IMetricVectorResult } from '../../../../../../../store/src/types/base-metric.types';
import { IMetricCell } from '../../../../../../../store/src/types/metric.types';
import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service';
import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component';
import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types';
import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers';
import { PaginationMonitorFactory } from '../../../../../shared/monitors/pagination-monitor.factory';
import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types';
import { CfCellHelper } from '../../../cf-cell.helpers';
import { ActiveRouteCfCell } from '../../../cf-page.types';
import { IMetricCell } from '../../../../../../../store/src/types/metric.types';
import { IMetricMatrixResult, IMetrics, IMetricVectorResult } from '../../../../../../../store/src/types/base-metric.types';
import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../../../store/src/actions/metrics.actions';
import { metricSchemaKey, entityFactory } from '../../../../../../../store/src/helpers/entity-factory';


export const enum CellMetrics {
HEALTHY = 'firehose_value_metric_rep_unhealthy_cell',
/**
* Deprecated since Diego v2.31.0. See https://github.com/bosh-prometheus/prometheus-boshrelease/issues/333
*/
HEALTHY_DEP = 'firehose_value_metric_rep_unhealthy_cell',
/**
* Available from Diego v2.31.0. See https://github.com/bosh-prometheus/prometheus-boshrelease/issues/333
*/
HEALTHY = 'firehose_value_metric_rep_garden_health_check_failed',
REMAINING_CONTAINERS = 'firehose_value_metric_rep_capacity_remaining_containers',
REMAINING_DISK = 'firehose_value_metric_rep_capacity_remaining_disk',
REMAINING_MEMORY = 'firehose_value_metric_rep_capacity_remaining_memory',
Expand All @@ -25,6 +36,10 @@ export const enum CellMetrics {
CPUS = 'firehose_value_metric_rep_num_cpus'
}


/**
* Designed to be used once drilled down to a cell (see ActiveRouteCfCell)
*/
@Injectable()
export class CloudFoundryCellService {

Expand All @@ -50,12 +65,13 @@ export class CloudFoundryCellService {

constructor(
activeRouteCfCell: ActiveRouteCfCell,
private entityServiceFactory: EntityServiceFactory) {
private entityServiceFactory: EntityServiceFactory,
store: Store<AppState>,
paginationMonitorFactory: PaginationMonitorFactory) {

this.cellId = activeRouteCfCell.cellId;
this.cfGuid = activeRouteCfCell.cfGuid;

this.healthy$ = this.generate(CellMetrics.HEALTHY);
this.remainingContainers$ = this.generate(CellMetrics.REMAINING_CONTAINERS);
this.totalContainers$ = this.generate(CellMetrics.TOTAL_CONTAINERS);
this.remainingDisk$ = this.generate(CellMetrics.REMAINING_DISK);
Expand All @@ -68,8 +84,19 @@ export class CloudFoundryCellService {
this.usageDisk$ = this.generateUsage(this.remainingDisk$, this.totalDisk$);
this.usageMemory$ = this.generateUsage(this.remainingMemory$, this.totalMemory$);

this.cellMetric$ = this.generate(CellMetrics.HEALTHY, true);

const cellHelper = new CfCellHelper(store, paginationMonitorFactory);
const action$ = cellHelper.createCellMetricAction(this.cfGuid);
this.cellMetric$ = action$.pipe(
switchMap(action => {
this.healthyMetricId = action.guid;
return this.generate(action.query.metric as CellMetrics, true);
})
);
this.healthy$ = action$.pipe(
switchMap(action => {
return this.generate(action.query.metric as CellMetrics, false);
})
);
}

public buildMetricConfig(
Expand Down Expand Up @@ -97,17 +124,14 @@ export class CloudFoundryCellService {
return lineChartConfig;
}

private generate(metric: CellMetrics, isMetric = false): Observable<any> {
const action = new FetchCFCellMetricsAction(
private generate(metric: CellMetrics, isMetric = false, customAction?: FetchCFCellMetricsAction): Observable<any> {
const action = customAction || new FetchCFCellMetricsAction(
this.cfGuid,
this.cellId,
new MetricQueryConfig(metric + `{bosh_job_id="${this.cellId}"}`, {}),
MetricQueryType.QUERY,
false
);
if (metric === CellMetrics.HEALTHY) {
this.healthyMetricId = action.guid;
}
return this.entityServiceFactory.create<IMetrics<IMetricVectorResult<IMetricCell>>>(
metricSchemaKey,
entityFactory(metricSchemaKey),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';

import { EndpointsService } from '../../../../core/endpoints.service';
import { AppState } from '../../../../../../store/src/app-state';
import {
CfCellsListConfigService,
} from '../../../../shared/components/list/list-types/cf-cells/cf-cells-list-config.service';
import { ListConfig } from '../../../../shared/components/list/list.component.types';
import { PaginationMonitorFactory } from '../../../../shared/monitors/pagination-monitor.factory';
import { CfCellHelper } from '../../cf-cell.helpers';
import { getActiveRouteCfCellProvider } from '../../cf.helpers';
import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoint.service';

Expand All @@ -24,7 +27,12 @@ import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoi
export class CloudFoundryCellsComponent {
hasCellMetrics$: Observable<boolean>;

constructor(endpointService: EndpointsService, cfEndpointService: CloudFoundryEndpointService) {
this.hasCellMetrics$ = endpointService.hasCellMetrics(cfEndpointService.cfGuid);
constructor(
cfEndpointService: CloudFoundryEndpointService,
store: Store<AppState>,
paginationMonitorFactory: PaginationMonitorFactory
) {
const cellHelper = new CfCellHelper(store, paginationMonitorFactory);
this.hasCellMetrics$ = cellHelper.hasCellMetrics(cfEndpointService.cfGuid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { Type } from '@angular/core';
import { Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { filter, first, map } from 'rxjs/operators';

import { AppState } from '../../../../store/src/app-state';
import { endpointSchemaKey } from '../../../../store/src/helpers/entity-factory';
import { selectEntities } from '../../../../store/src/selectors/api.selectors';
import { endpointsEntityRequestDataSelector } from '../../../../store/src/selectors/endpoint.selectors';
import { EndpointModel } from '../../../../store/src/types/endpoint.types';
import { ExtensionService } from '../../core/extension/extension-service';
import {
Expand Down Expand Up @@ -195,6 +196,15 @@ export function endpointHasMetrics(endpointGuid: string, store: Store<AppState>)
);
}

// There are two different methods for checking if an endpoint has metrics. Need to understand use cases
export function endpointHasMetricsByAvailable(store: Store<AppState>, endpointId: string): Observable<boolean> {
return store.select(endpointsEntityRequestDataSelector(endpointId)).pipe(
filter(endpoint => !!endpoint),
map(endpoint => endpoint.metricsAvailable),
first()
);
}

export function getEndpointAuthTypes() {
return endpointAuthTypes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import { AppState } from '../../../../../../../store/src/app-state';
import { entityFactory, metricSchemaKey } from '../../../../../../../store/src/helpers/entity-factory';
import { IMetricMatrixResult, IMetrics } from '../../../../../../../store/src/types/base-metric.types';
import { IMetricApplication } from '../../../../../../../store/src/types/metric.types';
import { EndpointsService } from '../../../../../core/endpoints.service';
import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service';
import { UtilsService } from '../../../../../core/utils.service';
import { ApplicationService } from '../../../../../features/applications/application.service';
import { CfCellHelper } from '../../../../../features/cloud-foundry/cf-cell.helpers';
import { PaginationMonitorFactory } from '../../../../monitors/pagination-monitor.factory';
import { MetricQueryType } from '../../../../services/metrics-range-selector.types';
import { ConfirmationDialogConfig } from '../../../confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../confirmation-dialog.service';
Expand Down Expand Up @@ -189,11 +190,12 @@ export class CfAppInstancesConfigService implements IListConfig<ListAppInstance>
private utilsService: UtilsService,
private router: Router,
private confirmDialog: ConfirmationDialogService,
private endpointsService: EndpointsService,
entityServiceFactory: EntityServiceFactory
entityServiceFactory: EntityServiceFactory,
paginationMonitorFactory: PaginationMonitorFactory
) {
const cellHelper = new CfCellHelper(store, paginationMonitorFactory);

this.initialised$ = this.endpointsService.hasCellMetrics(appService.cfGuid).pipe(
this.initialised$ = cellHelper.hasCellMetrics(appService.cfGuid).pipe(
map(hasMetrics => {
if (hasMetrics) {
this.columns.splice(1, 0, this.cfCellColumn);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { of } from 'rxjs';

import { ListView } from '../../../../../../../store/src/actions/list.actions';
import { IListDataSource } from '../../data-sources-controllers/list-data-source-types';
import { CardTypes } from '../../list-cards/card/card.component';
Expand All @@ -17,4 +19,5 @@ export class BaseCfListConfig<T> implements IListConfig<T> {
getMultiActions = () => [];
getSingleActions = () => [];
getMultiFiltersConfigs = () => [];
getInitialised = () => of(true);
}
Loading

0 comments on commit 173df4b

Please sign in to comment.