Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update metric used for cells #4009

Merged
merged 1 commit into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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