Skip to content
This repository was archived by the owner on Apr 11, 2023. It is now read-only.
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"js-yaml": "3.12.0",
"json-beautify": "1.0.1",
"lodash": "4.17.11",
"logfmt": "^1.2.1",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide some information on how and why this library is used? If I understand correctly it's also used in jaeger, but I'm not sure if we really need it, what I can see is that searchOptions.tags is expected to be a string in "logfmt" format and then is converted to plain json string to pass to jaeger, but what I fail to see is where these tags come from and if we can avoid having them in that format in the first place.

Also, I think you should remove the caret in lib version, as we don't use it in general.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logfmt is used in src/actions/JaegerThunkActions.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aljesusg can you also fix that in your new PR? (the caret version)

"patternfly": "3.59.1",
"patternfly-react": "2.25.3",
"patternfly-react-extensions": "2.16.8",
Expand Down
8 changes: 8 additions & 0 deletions src/actions/HelpDropdownThunkActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ThunkDispatch } from 'redux-thunk';
import { KialiAppState } from '../store/Store';
import { MessageType } from '../types/MessageCenter';
import { HelpDropdownActions } from './HelpDropdownActions';
import { JaegerActions } from './JaegerActions';
import { KialiAppAction } from './KialiAppAction';
import { MessageCenterActions } from './MessageCenterActions';
import * as API from '../services/Api';
Expand All @@ -18,6 +19,13 @@ const HelpDropdownThunkActions = {
status['data']['warningMessages']
)
);

// Get the jaeger URL
const hasJaeger = status['data']['externalServices'].filter(item => item['name'] === 'Jaeger');
if (hasJaeger.length === 1) {
dispatch(JaegerActions.setUrl(hasJaeger[0]['url']));
}

status['data']['warningMessages'].forEach(wMsg => {
dispatch(MessageCenterActions.addMessage(wMsg, 'systemErrors', MessageType.WARNING));
});
Expand Down
62 changes: 62 additions & 0 deletions src/actions/JaegerActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ActionType, createAction, createStandardAction } from 'typesafe-actions';

enum JaegerActionKeys {
SET_URL = 'SET_URL',
SERVICE_REQUEST_STARTED = 'SERVICE_REQUEST_STARTED',
SERVICE_SUCCESS = 'SERVICE_SUCCESS',
SERVICE_FAILED = 'SERVICE_FAILED',
SET_SERVICE = 'SET_SERVICE',
SET_NAMESPACE = 'SET_NAMESPACE',
SET_LOOKBACK = 'SET_LOOKBACK',
SET_LOOKBACK_CUSTOM = 'SET_LOOKBACK_CUSTOM',
SET_SEARCH_REQUEST = 'SET_SEARCH_REQUEST',
SET_TAGS = 'SET_TAGS',
SET_LIMIT = 'SET_LIMIT',
SET_DURATIONS = 'SET_DURATIONS',

// RESULTS VISUALZIATION OPTIONS
SET_SEARCH_GRAPH_TO_HIDE = 'SET_SEARCH_GRAPH_TO_HIDE',

// TRACE VISUALIZATION OPTIONS
SET_TRACE_MINIMAP_TO_SHOW = 'SET_TRACE_MINIMAP_TO_SHOW',
SET_TRACE_DETAILS_TO_SHOW = 'SET_TRACE_DETAILS_TO_SHOW'
}

// synchronous action creators
export const JaegerActions = {
setUrl: createAction(JaegerActionKeys.SET_URL, resolve => (url: string) =>
resolve({
url: url
})
),
requestStarted: createAction(JaegerActionKeys.SERVICE_REQUEST_STARTED),
requestFailed: createAction(JaegerActionKeys.SERVICE_FAILED),
receiveList: createAction(JaegerActionKeys.SERVICE_SUCCESS, resolve => (newList: string[]) =>
resolve({
list: newList
})
),
setService: createStandardAction(JaegerActionKeys.SET_SERVICE)<string>(),
setNamespace: createStandardAction(JaegerActionKeys.SET_NAMESPACE)<string>(),
setLookback: createStandardAction(JaegerActionKeys.SET_LOOKBACK)<string>(),
setTags: createStandardAction(JaegerActionKeys.SET_TAGS)<string>(),
setLimit: createStandardAction(JaegerActionKeys.SET_LIMIT)<number>(),
setSearchRequest: createStandardAction(JaegerActionKeys.SET_SEARCH_REQUEST)<string>(),
setSearchGraphToHide: createStandardAction(JaegerActionKeys.SET_SEARCH_GRAPH_TO_HIDE)<boolean>(),
setMinimapToShow: createStandardAction(JaegerActionKeys.SET_TRACE_MINIMAP_TO_SHOW)<boolean>(),
setDetailsToShow: createStandardAction(JaegerActionKeys.SET_TRACE_DETAILS_TO_SHOW)<boolean>(),
setCustomLookback: createAction(JaegerActionKeys.SET_LOOKBACK_CUSTOM, resolve => (start: string, end: string) =>
resolve({
start: start,
end: end
})
),
setDurations: createAction(JaegerActionKeys.SET_DURATIONS, resolve => (min: string, max: string) =>
resolve({
min: min,
max: max
})
)
};

export type JaegerAction = ActionType<typeof JaegerActions>;
125 changes: 125 additions & 0 deletions src/actions/JaegerThunkActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { JaegerActions } from './JaegerActions';

import * as Api from '../services/Api';

import { ServiceOverview } from '../types/ServiceList';
import { KialiAppState } from '../store/Store';
import { ThunkDispatch } from 'redux-thunk';
import { KialiAppAction } from './KialiAppAction';
import { JAEGER_QUERY } from '../config';
import logfmtParser from 'logfmt/lib/logfmt_parser';
import moment from 'moment';

export const convTagsLogfmt = (tags: string) => {
if (!tags) {
return null;
}
const data = logfmtParser.parse(tags);
Object.keys(data).forEach(key => {
const value = data[key];
// make sure all values are strings
// https://github.com/jaegertracing/jaeger/issues/550#issuecomment-352850811
if (typeof value !== 'string') {
data[key] = String(value);
}
});
return JSON.stringify(data);
};

export class JaegerURLSearch {
url: string;

constructor(url: string) {
this.url = `${url}${JAEGER_QUERY().PATH}?${JAEGER_QUERY().EMBED.UI_EMBED}=${JAEGER_QUERY().EMBED.VERSION}`;
}

addQueryParam(param: string, value: string | number) {
this.url += `&${param}=${value}`;
}

addParam(param: string) {
this.url += `&${param}`;
}
}

export const getUnixTimeStampInMSFromForm = (
startDate: string,
startDateTime: string,
endDate: string,
endDateTime: string
) => {
const start = `${startDate} ${startDateTime}`;
const end = `${endDate} ${endDateTime}`;
return {
start: `${moment(start, 'YYYY-MM-DD HH:mm').valueOf()}000`,
end: `${moment(end, 'YYYY-MM-DD HH:mm').valueOf()}000`
};
};

export const JaegerThunkActions = {
asyncFetchServices: (ns: string) => {
return (dispatch: ThunkDispatch<KialiAppState, void, KialiAppAction>, getState: () => KialiAppState) => {
if (getState()['authentication']['token'] === undefined) {
return Promise.resolve();
}
/** Get the token storage in redux-store */
const token = getState().authentication.token!.token;
/** generate Token */
const auth = `Bearer ${token}`;

// Dispatch a thunk from thunk!
dispatch(JaegerActions.requestStarted());
return Api.getServices(auth, ns)
.then(response => response['data'])
.then(data => {
const serviceList: string[] = [];
data['services'].forEach((aService: ServiceOverview) => {
serviceList.push(aService.name);
});
dispatch(JaegerActions.receiveList(serviceList));
})
.catch(() => dispatch(JaegerActions.requestFailed()));
};
},
getSearchURL: () => {
return (dispatch: ThunkDispatch<KialiAppState, void, KialiAppAction>, getState: () => KialiAppState) => {
const searchOptions = getState().jaegerState.search;
const jaegerOptions = JAEGER_QUERY().OPTIONS;
const urlRequest = new JaegerURLSearch(getState().jaegerState.jaegerURL);

// Search options
urlRequest.addQueryParam(jaegerOptions.START_TIME, searchOptions.start);
urlRequest.addQueryParam(jaegerOptions.END_TIME, searchOptions.end);
urlRequest.addQueryParam(jaegerOptions.LIMIT_TRACES, searchOptions.limit);
urlRequest.addQueryParam(jaegerOptions.LOOKBACK, searchOptions.lookback);
urlRequest.addQueryParam(jaegerOptions.MAX_DURATION, searchOptions.maxDuration);
urlRequest.addQueryParam(jaegerOptions.MIN_DURATION, searchOptions.minDuration);
urlRequest.addQueryParam(
jaegerOptions.SERVICE_SELECTOR,
searchOptions.serviceSelected + '.' + searchOptions.namespaceSelected
);
const logfmtTags = convTagsLogfmt(searchOptions.tags);
if (logfmtTags) {
urlRequest.addQueryParam(jaegerOptions.TAGS, logfmtTags);
}

// Embed Options
const traceOptions = getState().jaegerState.trace;

// Rename query params for 1.9 Jaeger
urlRequest.addQueryParam(JAEGER_QUERY().EMBED.UI_TRACE_HIDE_MINIMAP, traceOptions.hideMinimap ? '1' : '0');
urlRequest.addQueryParam(JAEGER_QUERY().EMBED.UI_SEARCH_HIDE_GRAPH, searchOptions.hideGraph ? '1' : '0');
urlRequest.addQueryParam(JAEGER_QUERY().EMBED.UI_TRACE_HIDE_SUMMARY, traceOptions.hideSummary ? '1' : '0');

return dispatch(JaegerActions.setSearchRequest(urlRequest.url));
};
},
setCustomLookback: (startDate: string, startTime: string, endDate: string, endTime: string) => {
return (dispatch: ThunkDispatch<KialiAppState, void, KialiAppAction>, getState: () => KialiAppState) => {
if (getState().jaegerState.search.lookback === 'custom') {
const toTimestamp = getUnixTimeStampInMSFromForm(startDate, startTime, endDate, endTime);
dispatch(JaegerActions.setCustomLookback(toTimestamp.start, toTimestamp.end));
}
};
}
};
4 changes: 3 additions & 1 deletion src/actions/KialiAppAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { MessageCenterAction } from './MessageCenterActions';
import { NamespaceAction } from './NamespaceAction';
import { UserSettingsAction } from './UserSettingsActions';
import { ServerConfigAction } from './ServerConfigActions';
import { JaegerAction } from './JaegerActions';

export type KialiAppAction =
| GlobalAction
Expand All @@ -21,4 +22,5 @@ export type KialiAppAction =
| MessageCenterAction
| NamespaceAction
| ServerConfigAction
| UserSettingsAction;
| UserSettingsAction
| JaegerAction;
43 changes: 43 additions & 0 deletions src/actions/__tests__/JaegerActions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { JaegerActions } from '../JaegerActions';
import { getType } from 'typesafe-actions';

describe('JaegerActions', () => {
it('should "update url" action', () => {
const showAction = JaegerActions.setUrl('jaeger-query-istio-system.127.0.0.1.nip.io');
expect(showAction.type).toEqual(getType(JaegerActions.setUrl));
expect(showAction.payload).toEqual({
url: 'jaeger-query-istio-system.127.0.0.1.nip.io'
});
});

it('should "receive list of services" action', () => {
const services = ['details', 'productpage'];
const showAction = JaegerActions.receiveList(services);
expect(showAction.type).toEqual(getType(JaegerActions.receiveList));
expect(showAction.payload).toEqual({
list: services
});
});

it('should "update custom lookback" action', () => {
const start = '1544432675600000';
const end = '1544432625600000';
const showAction = JaegerActions.setCustomLookback(start, end);
expect(showAction.type).toEqual(getType(JaegerActions.setCustomLookback));
expect(showAction.payload).toEqual({
start: start,
end: end
});
});

it('should "update durations" action', () => {
const min = '10ms';
const max = '10s';
const showAction = JaegerActions.setDurations(min, max);
expect(showAction.type).toEqual(getType(JaegerActions.setDurations));
expect(showAction.payload).toEqual({
min: min,
max: max
});
});
});
70 changes: 70 additions & 0 deletions src/actions/__tests__/JaegerThunkActions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { convTagsLogfmt, getUnixTimeStampInMSFromForm, JaegerURLSearch } from '../JaegerThunkActions';
import { JAEGER_QUERY } from '../../config';
import moment from 'moment-timezone';

describe('JaegerThunkActions', () => {
describe('Methods & Class', () => {
describe('Method convTagsLogfmt', () => {
it('convTagsLogfmt should return null if tags is empty', () => {
expect(convTagsLogfmt('')).toEqual(null);
});

it('convTagsLogfmt should JSON stringify of the tags', () => {
expect(convTagsLogfmt('error=true')).toEqual('{"error":"true"}');

expect(convTagsLogfmt('http.status_code=200 error=true')).toEqual('{"http.status_code":"200","error":"true"}');
});
});

describe('Method getUnixTimeStampInMSFromForm', () => {
it('getUnixTimeStampInMSFromForm should return the correct start and end time in MS', () => {
moment.tz.setDefault('UTC');
const date = '2019-01-01';
const startTime = '10:30';
const endTime = '11:30';
const result = getUnixTimeStampInMSFromForm(date, startTime, date, endTime);
expect(result.start).toEqual('1546338600000000');
expect(result.end).toEqual('1546342200000000');
});
});

describe('Class JaegerURLSearch', () => {
const url = 'https://jaeger-query-istio-system.127.0.0.1.nip.io';
let jaegerURLclass;

beforeEach(() => {
jaegerURLclass = new JaegerURLSearch(url);
});

it('JaegerURLSearch constructor', () => {
expect(jaegerURLclass.url).toEqual(
`${url}${JAEGER_QUERY().PATH}?${JAEGER_QUERY().EMBED.UI_EMBED}=${JAEGER_QUERY().EMBED.VERSION}`
);
});

it('JaegerURLSearch addQueryParam method with number value', () => {
jaegerURLclass.addQueryParam('uiTimelineHideMinimap', 1);
expect(jaegerURLclass.url).toEqual(
`${url}${JAEGER_QUERY().PATH}?${JAEGER_QUERY().EMBED.UI_EMBED}=${
JAEGER_QUERY().EMBED.VERSION
}&uiTimelineHideMinimap=1`
);
jaegerURLclass.addQueryParam('uiTimelineHideSummary', '0');
expect(jaegerURLclass.url).toEqual(
`${url}${JAEGER_QUERY().PATH}?${JAEGER_QUERY().EMBED.UI_EMBED}=${
JAEGER_QUERY().EMBED.VERSION
}&uiTimelineHideMinimap=1&uiTimelineHideSummary=0`
);
});

it('JaegerURLSearch addParam method', () => {
jaegerURLclass.addParam('uiTimelineHideMinimap');
expect(jaegerURLclass.url).toEqual(
`${url}${JAEGER_QUERY().PATH}?${JAEGER_QUERY().EMBED.UI_EMBED}=${
JAEGER_QUERY().EMBED.VERSION
}&uiTimelineHideMinimap`
);
});
});
});
});
3 changes: 3 additions & 0 deletions src/components/BreadcrumbView/BreadcrumbView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export class BreadcrumbView extends React.Component<BreadCumbViewProps, BreadCum
case 'out_metrics':
return 'Outbound Metrics';
break;
case 'traces':
return 'Traces';
break;
default:
return 'Info';
}
Expand Down
Loading