@spinnaker/kayenta 0.0.0-2025.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintrc.js +1 -0
  3. package/.huskyrc +5 -0
  4. package/.lintstagedrc.json +4 -0
  5. package/.prettierignore +4 -0
  6. package/.prettierrc.js +1 -0
  7. package/LICENSE.txt +203 -0
  8. package/README.md +81 -0
  9. package/__mocks__/styleMock.js +1 -0
  10. package/__mocks__/version.json +4 -0
  11. package/babel.config.js +3 -0
  12. package/build.gradle +67 -0
  13. package/build_scripts/checkLicenses.js +79 -0
  14. package/gradle.properties +0 -0
  15. package/jest.config.js +204 -0
  16. package/jest.setup.js +5 -0
  17. package/package.json +166 -0
  18. package/rollup-plugin-angularjs-template-loader.js +82 -0
  19. package/rollup.config.js +30 -0
  20. package/src/index.ts +2 -0
  21. package/src/kayenta/actions/creators.ts +163 -0
  22. package/src/kayenta/actions/index.ts +98 -0
  23. package/src/kayenta/canary.dataSource.bridge.ts +53 -0
  24. package/src/kayenta/canary.dataSource.stub.ts +64 -0
  25. package/src/kayenta/canary.help.ts +136 -0
  26. package/src/kayenta/canary.less +168 -0
  27. package/src/kayenta/canary.settings.ts +26 -0
  28. package/src/kayenta/canary.tsx +67 -0
  29. package/src/kayenta/components/canaryScore.component.less +77 -0
  30. package/src/kayenta/components/canaryScore.component.ts +12 -0
  31. package/src/kayenta/components/canaryScore.tsx +63 -0
  32. package/src/kayenta/components/canaryScores.component.ts +20 -0
  33. package/src/kayenta/components/canaryScores.less +22 -0
  34. package/src/kayenta/components/canaryScores.tsx +163 -0
  35. package/src/kayenta/components/loadStates.tsx +52 -0
  36. package/src/kayenta/domain/ICanaryConfig.ts +57 -0
  37. package/src/kayenta/domain/ICanaryConfigSummary.ts +7 -0
  38. package/src/kayenta/domain/ICanaryConfigUpdateResponse.ts +3 -0
  39. package/src/kayenta/domain/ICanaryExecutionStatusResult.ts +72 -0
  40. package/src/kayenta/domain/ICanaryJudgeResult.ts +51 -0
  41. package/src/kayenta/domain/ICanaryJudgeResultSummary.ts +5 -0
  42. package/src/kayenta/domain/ICanaryScoreThresholds.ts +4 -0
  43. package/src/kayenta/domain/IJudge.ts +5 -0
  44. package/src/kayenta/domain/IKayentaAccount.ts +14 -0
  45. package/src/kayenta/domain/IKayentaStageConfig.ts +58 -0
  46. package/src/kayenta/domain/IMetricSetPair.ts +17 -0
  47. package/src/kayenta/domain/IMetricsServiceMetadata.ts +2 -0
  48. package/src/kayenta/domain/ISetupCanaryStage.ts +11 -0
  49. package/src/kayenta/domain/MetricClassificationLabel.ts +8 -0
  50. package/src/kayenta/domain/ScoreClassificationLabel.ts +7 -0
  51. package/src/kayenta/domain/index.ts +15 -0
  52. package/src/kayenta/edit/changeMetricGroupModal.tsx +107 -0
  53. package/src/kayenta/edit/configDetail.tsx +31 -0
  54. package/src/kayenta/edit/configDetailActionButtons.tsx +24 -0
  55. package/src/kayenta/edit/configDetailHeader.tsx +104 -0
  56. package/src/kayenta/edit/configDetailLoadStates.tsx +36 -0
  57. package/src/kayenta/edit/configDetailLoader.tsx +60 -0
  58. package/src/kayenta/edit/configJson.less +42 -0
  59. package/src/kayenta/edit/configJsonModal.tsx +158 -0
  60. package/src/kayenta/edit/configList.less +7 -0
  61. package/src/kayenta/edit/configList.tsx +57 -0
  62. package/src/kayenta/edit/copyConfigButton.tsx +34 -0
  63. package/src/kayenta/edit/createConfigButton.tsx +34 -0
  64. package/src/kayenta/edit/deleteModal.tsx +87 -0
  65. package/src/kayenta/edit/edit.tsx +24 -0
  66. package/src/kayenta/edit/editMetricEffectSizes.tsx +186 -0
  67. package/src/kayenta/edit/editMetricModal.less +9 -0
  68. package/src/kayenta/edit/editMetricModal.spec.tsx +129 -0
  69. package/src/kayenta/edit/editMetricModal.tsx +294 -0
  70. package/src/kayenta/edit/editMetricValidation.spec.ts +63 -0
  71. package/src/kayenta/edit/editMetricValidation.ts +50 -0
  72. package/src/kayenta/edit/filterTemplateSelector.less +15 -0
  73. package/src/kayenta/edit/filterTemplateSelector.spec.tsx +106 -0
  74. package/src/kayenta/edit/filterTemplateSelector.tsx +194 -0
  75. package/src/kayenta/edit/filterTemplatesValidation.spec.ts +108 -0
  76. package/src/kayenta/edit/filterTemplatesValidation.ts +95 -0
  77. package/src/kayenta/edit/footer.less +30 -0
  78. package/src/kayenta/edit/footer.tsx +12 -0
  79. package/src/kayenta/edit/groupName.tsx +80 -0
  80. package/src/kayenta/edit/groupTabs.tsx +106 -0
  81. package/src/kayenta/edit/groupWeight.tsx +80 -0
  82. package/src/kayenta/edit/groupWeights.tsx +38 -0
  83. package/src/kayenta/edit/inlineTemplateEditor.spec.tsx +42 -0
  84. package/src/kayenta/edit/inlineTemplateEditor.tsx +61 -0
  85. package/src/kayenta/edit/judgeSelect.tsx +82 -0
  86. package/src/kayenta/edit/metricConfigurerDelegator.tsx +30 -0
  87. package/src/kayenta/edit/metricList.less +21 -0
  88. package/src/kayenta/edit/metricList.tsx +215 -0
  89. package/src/kayenta/edit/metricStoreSelector.tsx +66 -0
  90. package/src/kayenta/edit/nameAndDescription.tsx +90 -0
  91. package/src/kayenta/edit/openConfigJsonModalButton.tsx +33 -0
  92. package/src/kayenta/edit/openDeleteModalButton.tsx +50 -0
  93. package/src/kayenta/edit/ownedBy.tsx +34 -0
  94. package/src/kayenta/edit/save.tsx +19 -0
  95. package/src/kayenta/edit/saveConfigButton.tsx +65 -0
  96. package/src/kayenta/edit/saveConfigError.tsx +59 -0
  97. package/src/kayenta/edit/scoring.tsx +35 -0
  98. package/src/kayenta/edit/selectConfig.tsx +10 -0
  99. package/src/kayenta/edit/validationErrors.tsx +39 -0
  100. package/src/kayenta/index.ts +6 -0
  101. package/src/kayenta/layout/addNewButton.tsx +20 -0
  102. package/src/kayenta/layout/centeredDetail.tsx +13 -0
  103. package/src/kayenta/layout/deleteButton.tsx +11 -0
  104. package/src/kayenta/layout/disableable.tsx +87 -0
  105. package/src/kayenta/layout/formList.tsx +26 -0
  106. package/src/kayenta/layout/formRow.tsx +36 -0
  107. package/src/kayenta/layout/formattedDate.tsx +14 -0
  108. package/src/kayenta/layout/index.ts +2 -0
  109. package/src/kayenta/layout/keyValueList.less +20 -0
  110. package/src/kayenta/layout/keyValueList.tsx +114 -0
  111. package/src/kayenta/layout/list.less +9 -0
  112. package/src/kayenta/layout/list.spec.tsx +83 -0
  113. package/src/kayenta/layout/list.tsx +73 -0
  114. package/src/kayenta/layout/listDetail.tsx +33 -0
  115. package/src/kayenta/layout/radioChoice.tsx +29 -0
  116. package/src/kayenta/layout/styleguide.tsx +16 -0
  117. package/src/kayenta/layout/table/index.ts +5 -0
  118. package/src/kayenta/layout/table/nativeTable.tsx +51 -0
  119. package/src/kayenta/layout/table/nativeTableHeader.tsx +26 -0
  120. package/src/kayenta/layout/table/table.tsx +56 -0
  121. package/src/kayenta/layout/table/tableColumn.ts +7 -0
  122. package/src/kayenta/layout/table/tableHeader.tsx +23 -0
  123. package/src/kayenta/layout/tabs.tsx +26 -0
  124. package/src/kayenta/layout/titledSection.less +16 -0
  125. package/src/kayenta/layout/titledSection.tsx +20 -0
  126. package/src/kayenta/layout/titledSubsection.less +11 -0
  127. package/src/kayenta/layout/titledSubsection.tsx +22 -0
  128. package/src/kayenta/manualAnalysis/ManualAnalysisModal.tsx +716 -0
  129. package/src/kayenta/metricStore/atlas/atlasMetricConfigurer.tsx +130 -0
  130. package/src/kayenta/metricStore/atlas/index.ts +8 -0
  131. package/src/kayenta/metricStore/datadog/domain/IDatadogMetricDescriptor.ts +5 -0
  132. package/src/kayenta/metricStore/datadog/index.ts +9 -0
  133. package/src/kayenta/metricStore/datadog/metricConfigurer.tsx +90 -0
  134. package/src/kayenta/metricStore/datadog/metricTypeSelector.spec.tsx +59 -0
  135. package/src/kayenta/metricStore/datadog/metricTypeSelector.tsx +73 -0
  136. package/src/kayenta/metricStore/graphite/domain/IGraphiteMetricDescriptor.ts +5 -0
  137. package/src/kayenta/metricStore/graphite/index.ts +8 -0
  138. package/src/kayenta/metricStore/graphite/metricConfigurer.tsx +54 -0
  139. package/src/kayenta/metricStore/graphite/metricTypeSelector.tsx +80 -0
  140. package/src/kayenta/metricStore/graphite/typeahead.less +3 -0
  141. package/src/kayenta/metricStore/index.ts +8 -0
  142. package/src/kayenta/metricStore/metricStoreConfig.service.ts +12 -0
  143. package/src/kayenta/metricStore/newrelic/domain/INewRelicMetricDescriptor.ts +5 -0
  144. package/src/kayenta/metricStore/newrelic/index.ts +8 -0
  145. package/src/kayenta/metricStore/newrelic/metricConfigurer.tsx +58 -0
  146. package/src/kayenta/metricStore/prometheus/domain/IPrometheusCanaryMetricSetQueryConfig.ts +14 -0
  147. package/src/kayenta/metricStore/prometheus/domain/IPrometheusMetricDescriptor.ts +5 -0
  148. package/src/kayenta/metricStore/prometheus/index.ts +12 -0
  149. package/src/kayenta/metricStore/prometheus/metricConfigurer.tsx +157 -0
  150. package/src/kayenta/metricStore/prometheus/metricTypeSelector.less +5 -0
  151. package/src/kayenta/metricStore/prometheus/metricTypeSelector.spec.tsx +62 -0
  152. package/src/kayenta/metricStore/prometheus/metricTypeSelector.tsx +144 -0
  153. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.spec.ts +61 -0
  154. package/src/kayenta/metricStore/prometheus/queryTypeSelectors.ts +38 -0
  155. package/src/kayenta/metricStore/signalfx/domain/ISignalFxCanaryMetricSetQueryConfig.ts +7 -0
  156. package/src/kayenta/metricStore/signalfx/index.ts +8 -0
  157. package/src/kayenta/metricStore/signalfx/metricConfigurer.less +10 -0
  158. package/src/kayenta/metricStore/signalfx/metricConfigurer.tsx +187 -0
  159. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverCanaryMetricSetQueryConfig.ts +9 -0
  160. package/src/kayenta/metricStore/stackdriver/domain/IStackdriverMetricDescriptor.ts +17 -0
  161. package/src/kayenta/metricStore/stackdriver/index.ts +12 -0
  162. package/src/kayenta/metricStore/stackdriver/metricConfigurer.tsx +144 -0
  163. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.spec.tsx +92 -0
  164. package/src/kayenta/metricStore/stackdriver/metricTypeSelector.tsx +113 -0
  165. package/src/kayenta/middleware/actionInterceptor.ts +29 -0
  166. package/src/kayenta/middleware/asyncDispatch.ts +37 -0
  167. package/src/kayenta/middleware/epics.ts +211 -0
  168. package/src/kayenta/middleware/index.ts +3 -0
  169. package/src/kayenta/navigation/canary.states.stub.ts +28 -0
  170. package/src/kayenta/navigation/canary.states.ts +182 -0
  171. package/src/kayenta/reducers/app.ts +56 -0
  172. package/src/kayenta/reducers/asyncRequest.ts +5 -0
  173. package/src/kayenta/reducers/data.ts +169 -0
  174. package/src/kayenta/reducers/editingTemplate.ts +54 -0
  175. package/src/kayenta/reducers/group.ts +82 -0
  176. package/src/kayenta/reducers/index.ts +245 -0
  177. package/src/kayenta/reducers/prometheusMetricConfig.spec.ts +33 -0
  178. package/src/kayenta/reducers/prometheusMetricConfig.ts +56 -0
  179. package/src/kayenta/reducers/selectedConfig.spec.ts +190 -0
  180. package/src/kayenta/reducers/selectedConfig.ts +566 -0
  181. package/src/kayenta/reducers/selectedRun.ts +101 -0
  182. package/src/kayenta/reducers/signalFxMetricConfig.ts +36 -0
  183. package/src/kayenta/reducers/stackdriverMetricConfig.spec.ts +33 -0
  184. package/src/kayenta/reducers/stackdriverMetricConfig.ts +41 -0
  185. package/src/kayenta/reducers/templates.spec.ts +192 -0
  186. package/src/kayenta/reducers/validators.ts +118 -0
  187. package/src/kayenta/report/detail/allMetricResultsHeader.tsx +32 -0
  188. package/src/kayenta/report/detail/clickableHeader.tsx +21 -0
  189. package/src/kayenta/report/detail/colors.ts +47 -0
  190. package/src/kayenta/report/detail/detail.less +16 -0
  191. package/src/kayenta/report/detail/detail.tsx +48 -0
  192. package/src/kayenta/report/detail/detailLoader.tsx +55 -0
  193. package/src/kayenta/report/detail/graph/graph.tsx +37 -0
  194. package/src/kayenta/report/detail/graph/metricSetPairGraph.service.ts +35 -0
  195. package/src/kayenta/report/detail/graph/semiotic/boxplot.less +45 -0
  196. package/src/kayenta/report/detail/graph/semiotic/boxplot.tsx +283 -0
  197. package/src/kayenta/report/detail/graph/semiotic/chartHeader.tsx +19 -0
  198. package/src/kayenta/report/detail/graph/semiotic/chartLegend.less +26 -0
  199. package/src/kayenta/report/detail/graph/semiotic/chartLegend.tsx +42 -0
  200. package/src/kayenta/report/detail/graph/semiotic/circleIcon.tsx +16 -0
  201. package/src/kayenta/report/detail/graph/semiotic/config.less +5 -0
  202. package/src/kayenta/report/detail/graph/semiotic/config.ts +38 -0
  203. package/src/kayenta/report/detail/graph/semiotic/customAxisTickLabel.tsx +17 -0
  204. package/src/kayenta/report/detail/graph/semiotic/declarations/labella.d.ts +16 -0
  205. package/src/kayenta/report/detail/graph/semiotic/declarations/react-container-dimensions.d.ts +3 -0
  206. package/src/kayenta/report/detail/graph/semiotic/declarations/semiotic.d.ts +160 -0
  207. package/src/kayenta/report/detail/graph/semiotic/differenceArea.less +17 -0
  208. package/src/kayenta/report/detail/graph/semiotic/differenceArea.tsx +186 -0
  209. package/src/kayenta/report/detail/graph/semiotic/histogram.less +22 -0
  210. package/src/kayenta/report/detail/graph/semiotic/histogram.tsx +251 -0
  211. package/src/kayenta/report/detail/graph/semiotic/index.tsx +19 -0
  212. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.less +5 -0
  213. package/src/kayenta/report/detail/graph/semiotic/noValidDataSign.tsx +10 -0
  214. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.less +6 -0
  215. package/src/kayenta/report/detail/graph/semiotic/secondaryTSXAxis.tsx +58 -0
  216. package/src/kayenta/report/detail/graph/semiotic/semiotic.service.ts +32 -0
  217. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.less +53 -0
  218. package/src/kayenta/report/detail/graph/semiotic/semioticGraph.tsx +49 -0
  219. package/src/kayenta/report/detail/graph/semiotic/timeSeries.less +42 -0
  220. package/src/kayenta/report/detail/graph/semiotic/timeSeries.tsx +473 -0
  221. package/src/kayenta/report/detail/graph/semiotic/tooltip.tsx +55 -0
  222. package/src/kayenta/report/detail/graph/semiotic/utils.ts +90 -0
  223. package/src/kayenta/report/detail/graphTypeSelector.less +4 -0
  224. package/src/kayenta/report/detail/graphTypeSelector.tsx +50 -0
  225. package/src/kayenta/report/detail/groupScores.tsx +68 -0
  226. package/src/kayenta/report/detail/header.less +70 -0
  227. package/src/kayenta/report/detail/header.tsx +39 -0
  228. package/src/kayenta/report/detail/headerArrow.tsx +13 -0
  229. package/src/kayenta/report/detail/loadStates.tsx +31 -0
  230. package/src/kayenta/report/detail/metricResultActions.less +29 -0
  231. package/src/kayenta/report/detail/metricResultActions.tsx +87 -0
  232. package/src/kayenta/report/detail/metricResultClassification.tsx +22 -0
  233. package/src/kayenta/report/detail/metricResultDetail.tsx +20 -0
  234. package/src/kayenta/report/detail/metricResultDetailLayout.tsx +19 -0
  235. package/src/kayenta/report/detail/metricResultDeviation.tsx +25 -0
  236. package/src/kayenta/report/detail/metricResultStats.less +9 -0
  237. package/src/kayenta/report/detail/metricResultStats.tsx +120 -0
  238. package/src/kayenta/report/detail/metricResults.less +12 -0
  239. package/src/kayenta/report/detail/metricResults.tsx +52 -0
  240. package/src/kayenta/report/detail/metricResultsClassificationFilters.tsx +65 -0
  241. package/src/kayenta/report/detail/metricResultsColumns.tsx +27 -0
  242. package/src/kayenta/report/detail/metricResultsList.less +44 -0
  243. package/src/kayenta/report/detail/metricResultsList.tsx +120 -0
  244. package/src/kayenta/report/detail/metricSetPairLoadStates.tsx +22 -0
  245. package/src/kayenta/report/detail/multipleResultsTable.tsx +81 -0
  246. package/src/kayenta/report/detail/reportException.tsx +57 -0
  247. package/src/kayenta/report/detail/reportExplanation.less +12 -0
  248. package/src/kayenta/report/detail/reportExplanation.tsx +32 -0
  249. package/src/kayenta/report/detail/reportMetadata.tsx +167 -0
  250. package/src/kayenta/report/detail/reportScores.less +47 -0
  251. package/src/kayenta/report/detail/reportScores.tsx +80 -0
  252. package/src/kayenta/report/detail/score.tsx +33 -0
  253. package/src/kayenta/report/detail/sourceLinks.tsx +69 -0
  254. package/src/kayenta/report/list/configLink.tsx +32 -0
  255. package/src/kayenta/report/list/executionList.less +7 -0
  256. package/src/kayenta/report/list/loadStates.tsx +32 -0
  257. package/src/kayenta/report/list/pipelineLink.tsx +15 -0
  258. package/src/kayenta/report/list/reportLink.tsx +33 -0
  259. package/src/kayenta/report/list/table.tsx +309 -0
  260. package/src/kayenta/report/report.tsx +11 -0
  261. package/src/kayenta/selectors/filterTemplatesSelectors.ts +87 -0
  262. package/src/kayenta/selectors/index.ts +62 -0
  263. package/src/kayenta/service/canaryConfig.service.ts +122 -0
  264. package/src/kayenta/service/canaryRun.service.ts +60 -0
  265. package/src/kayenta/service/delegateFactory.ts +24 -0
  266. package/src/kayenta/service/metricsServiceMetadata.service.ts +9 -0
  267. package/src/kayenta/stages/kayentaStage/AnalysisType.spec.tsx +47 -0
  268. package/src/kayenta/stages/kayentaStage/AnalysisType.tsx +49 -0
  269. package/src/kayenta/stages/kayentaStage/CanaryExecutionLabel.tsx +26 -0
  270. package/src/kayenta/stages/kayentaStage/analysisType.component.ts +12 -0
  271. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.component.ts +12 -0
  272. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.less +5 -0
  273. package/src/kayenta/stages/kayentaStage/canaryRunSummaries.tsx +136 -0
  274. package/src/kayenta/stages/kayentaStage/forAnalysisType.component.ts +45 -0
  275. package/src/kayenta/stages/kayentaStage/kayentaStage.controller.ts +789 -0
  276. package/src/kayenta/stages/kayentaStage/kayentaStage.html +528 -0
  277. package/src/kayenta/stages/kayentaStage/kayentaStage.less +5 -0
  278. package/src/kayenta/stages/kayentaStage/kayentaStage.transformer.ts +179 -0
  279. package/src/kayenta/stages/kayentaStage/kayentaStage.ts +221 -0
  280. package/src/kayenta/stages/kayentaStage/kayentaStageConfigSection.component.ts +21 -0
  281. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.controller.ts +88 -0
  282. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.html +114 -0
  283. package/src/kayenta/stages/kayentaStage/kayentaStageExecutionDetails.less +6 -0
  284. package/src/kayenta/stages/kayentaStage/stageTypes.ts +5 -0
  285. package/src/kayenta/utils/duration.spec.ts +69 -0
  286. package/src/kayenta/utils/duration.ts +48 -0
  287. package/src/lazy.ts +29 -0
  288. package/src/stub.ts +60 -0
  289. package/tsconfig.json +11 -0
@@ -0,0 +1,179 @@
1
+ import { get, last, round } from 'lodash';
2
+
3
+ import { Application, IExecution, IExecutionStage, ITransformer, OrchestratedItemTransformer } from '@spinnaker/core';
4
+
5
+ import { CREATE_SERVER_GROUP, DEPLOY_CANARY_SERVER_GROUPS, KAYENTA_CANARY, RUN_CANARY, WAIT } from './stageTypes';
6
+
7
+ const stageTypesToAlwaysShow = [KAYENTA_CANARY, CREATE_SERVER_GROUP];
8
+
9
+ export class KayentaStageTransformer implements ITransformer {
10
+ public transform(_application: Application, execution: IExecution): void {
11
+ const kayentaStages = execution.stages.filter(({ type }) => type === KAYENTA_CANARY);
12
+ if (!kayentaStages.length) {
13
+ return;
14
+ }
15
+
16
+ let stagesToRenderAsTasks: IExecutionStage[] = [];
17
+
18
+ execution.stages.forEach((stage) => {
19
+ if (stage.type === KAYENTA_CANARY) {
20
+ OrchestratedItemTransformer.defineProperties(stage);
21
+
22
+ const intervalStageId: string = stage.context.intervalStageId;
23
+ const syntheticCanaryStages = intervalStageId
24
+ ? execution.stages.filter(
25
+ ({ parentStageId, type }) =>
26
+ parentStageId && parentStageId === intervalStageId && [WAIT, RUN_CANARY].includes(type),
27
+ )
28
+ : [];
29
+
30
+ stagesToRenderAsTasks = stagesToRenderAsTasks.concat(syntheticCanaryStages);
31
+
32
+ const runCanaryStages = syntheticCanaryStages.filter((s) => s.type === RUN_CANARY);
33
+ syntheticCanaryStages.forEach((syntheticStage) => OrchestratedItemTransformer.defineProperties(syntheticStage));
34
+ this.calculateRunCanaryResults(runCanaryStages);
35
+ this.calculateKayentaCanaryResults(stage, syntheticCanaryStages);
36
+
37
+ stage.exceptions = [];
38
+ this.addExceptions([stage, ...syntheticCanaryStages], stage.exceptions);
39
+
40
+ // For now, a 'kayentaCanary' stage should only have an 'aggregateCanaryResults' task, which should definitely go last.
41
+ stage.tasks = [...syntheticCanaryStages, ...stage.tasks];
42
+ } else if (stage.type === CREATE_SERVER_GROUP && this.isDescendantOf(stage, kayentaStages, execution)) {
43
+ OrchestratedItemTransformer.defineProperties(stage);
44
+ const parentKayentaStageId = this.isDescendantOf(stage, kayentaStages, execution);
45
+ const locations = stage.context['deploy.server.groups'] && Object.keys(stage.context['deploy.server.groups']);
46
+ stage.name = `Deploy ${stage.context.freeFormDetails}${locations ? ' in ' + locations.join(', ') : ''}`;
47
+ stage.parentStageId = parentKayentaStageId;
48
+ }
49
+ });
50
+
51
+ const kayentaStageIds = kayentaStages.map(({ id }) => id);
52
+ const deployCanaryServerGroupsStages = execution.stages.filter(
53
+ ({ parentStageId, type }) => kayentaStageIds.includes(parentStageId) && type === DEPLOY_CANARY_SERVER_GROUPS,
54
+ );
55
+
56
+ deployCanaryServerGroupsStages.forEach((deployCanaryStage) => {
57
+ const parentKayentaStage = kayentaStages.find(({ id }) => deployCanaryStage.parentStageId === id);
58
+ if (parentKayentaStage && (deployCanaryStage.outputs.deployedServerGroups || []).length) {
59
+ const [{ controlScope, experimentScope }] = deployCanaryStage.outputs.deployedServerGroups;
60
+ parentKayentaStage.outputs = { ...parentKayentaStage.outputs, controlScope, experimentScope };
61
+ }
62
+ });
63
+ execution.stages = execution.stages.filter(
64
+ (stage) =>
65
+ !stagesToRenderAsTasks.includes(stage) &&
66
+ (!this.isDescendantOf(stage, kayentaStages, execution) ||
67
+ stageTypesToAlwaysShow.includes(stage.type) ||
68
+ stage.status !== 'SUCCEEDED'),
69
+ );
70
+ }
71
+
72
+ private isDescendantOf(child: IExecutionStage, ancestors: IExecutionStage[], execution: IExecution) {
73
+ const ancestorIds = ancestors.map(({ id }) => id);
74
+ let node = child;
75
+ while (node && node.parentStageId) {
76
+ const parentNode = execution.stages.find(({ id }) => node.parentStageId === id);
77
+ if (parentNode && ancestorIds.includes(parentNode.id)) {
78
+ return parentNode.id;
79
+ } else {
80
+ node = parentNode;
81
+ }
82
+ }
83
+
84
+ return null;
85
+ }
86
+
87
+ // Massages each runCanary stage into what the `canaryScore` component expects.
88
+ private calculateRunCanaryResults(runCanaryStages: IExecutionStage[]): void {
89
+ runCanaryStages.forEach((run) => {
90
+ if (typeof run.getValueFor('canaryScore') === 'number') {
91
+ if (run.status === 'SUCCEEDED') {
92
+ if (run.context.canaryScore >= run.context.scoreThresholds.pass) {
93
+ run.result = 'success';
94
+ }
95
+ } else {
96
+ run.health = 'unhealthy';
97
+ }
98
+
99
+ run.context.canaryScore = round(run.context.canaryScore, 2);
100
+ }
101
+ });
102
+ }
103
+
104
+ // Massages the kayentaCanary stage results into what the `canaryScore` component expects.
105
+ private calculateKayentaCanaryResults(kayentaStage: IExecutionStage, runCanaryStages: IExecutionStage[]): void {
106
+ if (!kayentaStage.isRunning) {
107
+ if (kayentaStage.getValueFor('canaryScores')) {
108
+ // If we made it through the final scheduled canary run, this should
109
+ // be the same as the value returned from `getLastCanaryRunScore`,
110
+ // but this is also how Orca determines the overall score.
111
+ kayentaStage.context.overallScore = last(kayentaStage.getValueFor('canaryScores'));
112
+ } else {
113
+ kayentaStage.context.overallScore = this.getLastCanaryRunScore(runCanaryStages);
114
+ }
115
+ kayentaStage.context.overallScore = round(kayentaStage.context.overallScore, 2);
116
+
117
+ // Sometimes when the very first runCanary stage fails due to a low score
118
+ // it never adds its canaryScoreMessage to the kayenta stage.
119
+ if (!kayentaStage.context.canaryScoreMessage) {
120
+ kayentaStage.context.canaryScoreMessage = this.getLastCanaryScoreMessage(runCanaryStages);
121
+ }
122
+
123
+ if (!kayentaStage.isCanceled) {
124
+ const overallScore = get(kayentaStage, 'context.overallScore', null);
125
+ const scoreThreshold = get(kayentaStage, 'context.canaryConfig.scoreThresholds.marginal', null);
126
+ if (kayentaStage.status === 'SUCCEEDED' || (overallScore && overallScore > scoreThreshold)) {
127
+ kayentaStage.context.overallResult = 'success';
128
+ } else {
129
+ kayentaStage.context.overallHealth = 'unhealthy';
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ private getLastCanaryRunScore(runCanaryStages: IExecutionStage[] = []): number {
136
+ const canaryRunScores = runCanaryStages
137
+ .filter((s) => typeof s.getValueFor('canaryScore') === 'number')
138
+ .map((s) => s.getValueFor('canaryScore'));
139
+ return last(canaryRunScores);
140
+ }
141
+
142
+ private getLastCanaryScoreMessage(runCanaryStages: IExecutionStage[] = []): number {
143
+ const canaryRunMessages = runCanaryStages
144
+ .filter((s) => s.getValueFor('canaryScoreMessage'))
145
+ .map((s) => s.getValueFor('canaryScoreMessage'));
146
+ return last(canaryRunMessages);
147
+ }
148
+
149
+ private addExceptions(stages: IExecutionStage[], exceptions: string[]): void {
150
+ stages.forEach((stage) => {
151
+ if (this.getException(stage)) {
152
+ exceptions.push(this.getException(stage));
153
+ }
154
+ const overallScore = get(stage, 'context.overallScore', null);
155
+ const scoreThreshold = get(stage, 'context.canaryConfig.scoreThresholds.marginal', null);
156
+ const message = get(stage, 'context.canaryScoreMessage', null);
157
+ if (overallScore && message && overallScore <= scoreThreshold) {
158
+ exceptions.push(message);
159
+ }
160
+ });
161
+ }
162
+
163
+ private getException(stage: IExecutionStage): string {
164
+ if (stage && stage.isFailed) {
165
+ if (
166
+ stage.context &&
167
+ stage.context.exception &&
168
+ stage.context.exception.details &&
169
+ stage.context.exception.details.responseBody
170
+ ) {
171
+ return stage.context.exception.details.responseBody;
172
+ } else {
173
+ return stage.failureMessage;
174
+ }
175
+ } else {
176
+ return null;
177
+ }
178
+ }
179
+ }
@@ -0,0 +1,221 @@
1
+ import { CanarySettings } from 'kayenta/canary.settings';
2
+ import { IKayentaStage, KayentaAnalysisType } from 'kayenta/domain';
3
+ import { getCanaryConfigById } from 'kayenta/service/canaryConfig.service';
4
+ import { difference, get, has, isEmpty, isString, map, uniq } from 'lodash';
5
+
6
+ import { IPipeline } from '@spinnaker/core';
7
+
8
+ import { CanaryExecutionLabel } from './CanaryExecutionLabel';
9
+
10
+ const isExpression = (value: string) => isString(value) && value.includes('${');
11
+
12
+ const emailPattern = /^(.+)@(.+).([A-Za-z]{2,6})/;
13
+ const isValidEmail = (email: string) => isExpression(email) || email.match(emailPattern);
14
+
15
+ const utcInstantPattern = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/;
16
+ const isValidUtcInstant = (timestamp: string) =>
17
+ isExpression(timestamp) || (isString(timestamp) && timestamp.match(utcInstantPattern));
18
+
19
+ const requiredForAnalysisTypes = (
20
+ analysisTypes: KayentaAnalysisType[] = [],
21
+ fieldName: string,
22
+ fieldLabel?: string,
23
+ ): ((p: IPipeline, s: IKayentaStage) => string) => {
24
+ return (_pipeline: IPipeline, stage: IKayentaStage): string => {
25
+ if (analysisTypes.includes(stage.analysisType)) {
26
+ if (!has(stage, fieldName) || get(stage, fieldName) === '') {
27
+ return `<strong>${fieldLabel || fieldName}</strong> is a required field for Kayenta Canary stages.`;
28
+ }
29
+ }
30
+ return null;
31
+ };
32
+ };
33
+
34
+ const allScopesMustBeConfigured = (_pipeline: IPipeline, stage: IKayentaStage): PromiseLike<string> => {
35
+ return getCanaryConfigById(get(stage, 'canaryConfig.canaryConfigId')).then((configDetails) => {
36
+ let definedScopeNames = uniq(map(configDetails.metrics, (metric) => metric.scopeName || 'default'));
37
+ definedScopeNames = !isEmpty(definedScopeNames) ? definedScopeNames : ['default'];
38
+
39
+ const configureScopedNames: string[] = map(get(stage, 'canaryConfig.scopes'), 'scopeName');
40
+ const missingScopeNames = difference(definedScopeNames, configureScopedNames);
41
+
42
+ if (missingScopeNames.length > 1) {
43
+ return `Scopes <strong>${missingScopeNames.join()}</strong> are defined but not configured.`;
44
+ } else if (missingScopeNames.length === 1) {
45
+ return `Scope <strong>${missingScopeNames[0]}</strong> is defined but not configured.`;
46
+ } else {
47
+ return null;
48
+ }
49
+ });
50
+ };
51
+
52
+ const allConfiguredScopesMustBeDefined = (_pipeline: IPipeline, stage: IKayentaStage): PromiseLike<string> => {
53
+ return getCanaryConfigById(get(stage, 'canaryConfig.canaryConfigId')).then((configDetails) => {
54
+ let definedScopeNames = uniq(map(configDetails.metrics, (metric) => metric.scopeName || 'default'));
55
+ definedScopeNames = !isEmpty(definedScopeNames) ? definedScopeNames : ['default'];
56
+
57
+ const configureScopedNames: string[] = map(get(stage, 'canaryConfig.scopes'), 'scopeName');
58
+ const missingScopeNames = difference(configureScopedNames, definedScopeNames);
59
+
60
+ if (missingScopeNames.length > 1) {
61
+ return `Scopes <strong>${missingScopeNames.join()}</strong> are configured but are not defined in the canary configuration.`;
62
+ } else if (missingScopeNames.length === 1) {
63
+ return `Scope <strong>${missingScopeNames[0]}</strong> is configured but is not defined in the canary configuration.`;
64
+ } else {
65
+ return null;
66
+ }
67
+ });
68
+ };
69
+
70
+ export const kayentaCanaryStage = {
71
+ label: CanarySettings.stageName || 'Canary Analysis',
72
+ description: CanarySettings.stageDescription || 'Runs a canary task',
73
+ key: 'kayentaCanary',
74
+ templateUrl: require('./kayentaStage.html'),
75
+ controller: 'KayentaCanaryStageCtrl',
76
+ controllerAs: 'kayentaCanaryStageCtrl',
77
+ executionDetailsUrl: require('./kayentaStageExecutionDetails.html'),
78
+ executionLabelComponent: CanaryExecutionLabel,
79
+ validators: [
80
+ { type: 'requiredField', fieldName: 'canaryConfig.canaryConfigId', fieldLabel: 'Config Name' },
81
+ { type: 'requiredField', fieldName: 'canaryConfig.metricsAccountName', fieldLabel: 'Metrics Account' },
82
+ { type: 'requiredField', fieldName: 'canaryConfig.storageAccountName', fieldLabel: 'Storage Account' },
83
+ {
84
+ type: 'custom',
85
+ validate: requiredForAnalysisTypes(
86
+ [KayentaAnalysisType.RealTimeAutomatic],
87
+ 'deployments.serverGroupPairs[0].control',
88
+ 'Baseline & Canary Server Groups',
89
+ ),
90
+ },
91
+ {
92
+ type: 'custom',
93
+ validate: requiredForAnalysisTypes(
94
+ [KayentaAnalysisType.RealTimeAutomatic],
95
+ 'deployments.baseline.cluster',
96
+ 'Baseline Cluster',
97
+ ),
98
+ },
99
+ {
100
+ type: 'custom',
101
+ validate: requiredForAnalysisTypes(
102
+ [KayentaAnalysisType.RealTimeAutomatic],
103
+ 'deployments.baseline.account',
104
+ 'Baseline Account',
105
+ ),
106
+ },
107
+ {
108
+ type: 'custom',
109
+ validate: requiredForAnalysisTypes(
110
+ [KayentaAnalysisType.RealTime, KayentaAnalysisType.Retrospective],
111
+ 'canaryConfig.scopes[0].controlScope',
112
+ 'Baseline Scope',
113
+ ),
114
+ },
115
+ {
116
+ type: 'custom',
117
+ validate: requiredForAnalysisTypes(
118
+ [KayentaAnalysisType.RealTime, KayentaAnalysisType.Retrospective],
119
+ 'canaryConfig.scopes[0].experimentScope',
120
+ 'Canary Scope',
121
+ ),
122
+ },
123
+ {
124
+ type: 'custom',
125
+ validate: requiredForAnalysisTypes(
126
+ [KayentaAnalysisType.RealTime, KayentaAnalysisType.RealTimeAutomatic],
127
+ 'canaryConfig.lifetimeDuration',
128
+ 'Lifetime',
129
+ ),
130
+ },
131
+ {
132
+ type: 'custom',
133
+ validate: requiredForAnalysisTypes(
134
+ [KayentaAnalysisType.Retrospective],
135
+ 'canaryConfig.scopes[0].startTimeIso',
136
+ 'Start Time',
137
+ ),
138
+ },
139
+ {
140
+ type: 'custom',
141
+ validate: requiredForAnalysisTypes(
142
+ [KayentaAnalysisType.Retrospective],
143
+ 'canaryConfig.scopes[0].endTimeIso',
144
+ 'End Time',
145
+ ),
146
+ },
147
+ {
148
+ type: 'custom',
149
+ validate: (_pipeline: IPipeline, stage: IKayentaStage) => {
150
+ if (
151
+ !has(stage, 'canaryConfig.canaryConfigId') ||
152
+ stage.analysisType === KayentaAnalysisType.RealTimeAutomatic
153
+ ) {
154
+ return null;
155
+ }
156
+
157
+ return getCanaryConfigById(get(stage, 'canaryConfig.canaryConfigId')).then((configDetails) => {
158
+ if (
159
+ get(configDetails, 'metrics[0].query.type') === 'atlas' &&
160
+ !get(stage, 'canaryConfig.scopes[0].extendedScopeParams.type')
161
+ ) {
162
+ return 'Scope Type is required';
163
+ } else {
164
+ return null;
165
+ }
166
+ });
167
+ },
168
+ },
169
+ {
170
+ type: 'custom',
171
+ validate: allScopesMustBeConfigured,
172
+ },
173
+ {
174
+ type: 'custom',
175
+ validate: allConfiguredScopesMustBeDefined,
176
+ },
177
+ {
178
+ type: 'custom',
179
+ validate: (_pipeline: IPipeline, { canaryConfig }: IKayentaStage) => {
180
+ if (!CanarySettings.legacySiteLocalFieldsEnabled) {
181
+ return null;
182
+ }
183
+ const notificationEmail = get(canaryConfig, 'siteLocal.notificationEmail');
184
+ if (!notificationEmail) {
185
+ return null;
186
+ }
187
+ const emails = Array.isArray(notificationEmail) ? notificationEmail : [notificationEmail];
188
+ const invalidEmail = emails.find((email) => !isValidEmail(email));
189
+ return invalidEmail ? `Invalid <strong>Notification Email</strong> (${invalidEmail})` : null;
190
+ },
191
+ },
192
+ {
193
+ type: 'custom',
194
+ validate: (_pipeline: IPipeline, stage: IKayentaStage) => {
195
+ const startTime: string = get(stage, 'canaryConfig.scopes[0].startTimeIso');
196
+ if (
197
+ stage.analysisType === KayentaAnalysisType.Retrospective &&
198
+ !isEmpty(startTime) &&
199
+ !isValidUtcInstant(startTime)
200
+ ) {
201
+ return '<strong>Start Time</strong> must be formatted as a UTC instant using ISO-8601 instant format (e.g., 2018-07-12T20:28:29Z).';
202
+ }
203
+ return null;
204
+ },
205
+ },
206
+ {
207
+ type: 'custom',
208
+ validate: (_pipeline: IPipeline, stage: IKayentaStage) => {
209
+ const endTime: string = get(stage, 'canaryConfig.scopes[0].endTimeIso');
210
+ if (
211
+ stage.analysisType === KayentaAnalysisType.Retrospective &&
212
+ !isEmpty(endTime) &&
213
+ !isValidUtcInstant(endTime)
214
+ ) {
215
+ return '<strong>End Time</strong> must be formatted as a UTC instant using ISO-8601 instant format (e.g., 2018-07-12T20:28:29Z).';
216
+ }
217
+ return null;
218
+ },
219
+ },
220
+ ],
221
+ };
@@ -0,0 +1,21 @@
1
+ import { IComponentOptions, module } from 'angular';
2
+
3
+ const kayentaStageConfigSection: IComponentOptions = {
4
+ transclude: {
5
+ sectionHeader: '?sectionHeader',
6
+ },
7
+ bindings: { title: '@' },
8
+ template: `
9
+ <section>
10
+ <ul class="list-inline">
11
+ <li><h5>{{ $ctrl.title }}</h5></li>
12
+ <li><span ng-transclude="sectionHeader"></span></li>
13
+ </ul>
14
+ <div class="horizontal-rule"></div>
15
+ <ng-transclude></ng-transclude>
16
+ </section>
17
+ `,
18
+ };
19
+
20
+ export const KAYENTA_STAGE_CONFIG_SECTION = 'spinnaker.kayenta.stageConfigSection';
21
+ module(KAYENTA_STAGE_CONFIG_SECTION, []).component('kayentaStageConfigSection', kayentaStageConfigSection);
@@ -0,0 +1,88 @@
1
+ import { StateParams } from '@uirouter/angularjs';
2
+ import { IScope, module } from 'angular';
3
+ import { ICanaryConfigSummary, KayentaAnalysisType } from 'kayenta/domain';
4
+
5
+ import { ExecutionDetailsSectionService, IExecutionStage } from '@spinnaker/core';
6
+
7
+ import { CANARY_RUN_SUMMARIES_COMPONENT } from './canaryRunSummaries.component';
8
+ import { KAYENTA_CANARY, RUN_CANARY } from './stageTypes';
9
+
10
+ import './kayentaStageExecutionDetails.less';
11
+
12
+ class KayentaStageExecutionDetailsController {
13
+ public static $inject = ['$scope', '$stateParams', 'executionDetailsSectionService'];
14
+
15
+ public canaryRuns: IExecutionStage[];
16
+ public canaryConfigName: string;
17
+ public firstScopeName: string;
18
+ public resolvedControl: string;
19
+ public resolvedExperiment: string;
20
+
21
+ constructor(
22
+ public $scope: IScope,
23
+ private $stateParams: StateParams,
24
+ private executionDetailsSectionService: ExecutionDetailsSectionService,
25
+ ) {
26
+ 'ngInject';
27
+ this.$scope.configSections = ['canarySummary', 'canaryConfig', 'taskStatus'];
28
+ this.$scope.$on('$stateChangeSuccess', () => this.initialize());
29
+ }
30
+
31
+ public $onInit(): void {
32
+ this.initialize();
33
+ }
34
+
35
+ private initialize(): void {
36
+ this.executionDetailsSectionService.synchronizeSection(this.$scope.configSections, () => this.initialized());
37
+ }
38
+
39
+ private initialized(): void {
40
+ this.$scope.detailsSection = this.$stateParams.details;
41
+ this.$scope.application.ready().then(() => {
42
+ if (this.$scope.stage.type !== KAYENTA_CANARY) {
43
+ return;
44
+ }
45
+ const canaryConfigSummary = this.$scope.application
46
+ .getDataSource('canaryConfigs')
47
+ .data.find(
48
+ (config: ICanaryConfigSummary) => config.id === this.$scope.stage.context.canaryConfig.canaryConfigId,
49
+ );
50
+ if (canaryConfigSummary) {
51
+ this.canaryConfigName = canaryConfigSummary.name;
52
+ }
53
+ });
54
+ this.setCanaryRuns();
55
+ this.resolveFirstScopeName();
56
+ this.resolveControlAndExperimentNames();
57
+ this.$scope.$watchCollection('stage.tasks', () => this.setCanaryRuns());
58
+ }
59
+
60
+ private setCanaryRuns(): void {
61
+ // The kayentaStageTransformer pushes related 'runCanary' and 'wait' stages
62
+ // into the 'kayentaCanary' tasks list.
63
+ this.canaryRuns = this.$scope.stage.tasks.filter((t: IExecutionStage) => t.type === RUN_CANARY);
64
+ }
65
+
66
+ private resolveFirstScopeName(): void {
67
+ this.firstScopeName = this.$scope.stage.context.canaryConfig.scopes[0].scopeName;
68
+ }
69
+
70
+ private resolveControlAndExperimentNames(): void {
71
+ if (this.$scope.stage.context.analysisType === KayentaAnalysisType.RealTimeAutomatic) {
72
+ this.resolvedControl = this.$scope.stage.outputs.controlScope;
73
+ this.resolvedExperiment = this.$scope.stage.outputs.experimentScope;
74
+ } else {
75
+ this.resolvedControl = this.canaryRuns.length
76
+ ? this.canaryRuns[0].context.scopes[this.firstScopeName].controlScope.scope
77
+ : this.$scope.stage.context.canaryConfig.scopes[0].controlScope;
78
+ this.resolvedExperiment = this.canaryRuns.length
79
+ ? this.canaryRuns[0].context.scopes[this.firstScopeName].experimentScope.scope
80
+ : this.$scope.stage.context.canaryConfig.scopes[0].experimentScope;
81
+ }
82
+ }
83
+ }
84
+
85
+ export const KAYENTA_STAGE_EXECUTION_DETAILS_CONTROLLER = 'spinnaker.kayenta.kayentaStageExecutionDetails.controller';
86
+ module(KAYENTA_STAGE_EXECUTION_DETAILS_CONTROLLER, [CANARY_RUN_SUMMARIES_COMPONENT])
87
+ .controller('kayentaStageExecutionDetailsCtrl', KayentaStageExecutionDetailsController)
88
+ .filter('dateToMillis', () => Date.parse);
@@ -0,0 +1,114 @@
1
+ <div class="canary-details" ng-controller="kayentaStageExecutionDetailsCtrl as kayentaStageDetailsCtrl">
2
+ <execution-details-section-nav sections="configSections"></execution-details-section-nav>
3
+ <div class="step-section-details" ng-if="detailsSection === 'canarySummary'">
4
+ <div class="row">
5
+ <div class="col-md-2 canary-summary">
6
+ <div class="score score-large">
7
+ <kayenta-canary-score
8
+ score="stage.context.overallScore"
9
+ health="stage.context.overallHealth"
10
+ result="stage.context.overallResult"
11
+ ></kayenta-canary-score>
12
+ </div>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="row">
17
+ <div class="col-md-12 horizontal">
18
+ <canary-run-summaries
19
+ class="horizontal flex-1"
20
+ canary-runs="kayentaStageDetailsCtrl.canaryRuns"
21
+ first-scope-name="kayentaStageDetailsCtrl.firstScopeName"
22
+ ></canary-run-summaries>
23
+ </div>
24
+ </div>
25
+
26
+ <stage-failure-message
27
+ stage="stage"
28
+ is-failed="stage.exceptions.length > 0"
29
+ messages="stage.exceptions"
30
+ ></stage-failure-message>
31
+ </div>
32
+
33
+ <div class="step-section-details" ng-if="detailsSection === 'canaryConfig'">
34
+ <div class="row">
35
+ <div class="col-md-12 canary-config-section">
36
+ <h5>Scope</h5>
37
+ <div class="horizontal-rule"></div>
38
+ <div class="row">
39
+ <div class="col-md-4 sm-label-right compact">Baseline</div>
40
+ <div class="col-md-8" style="word-wrap: break-word">{{ kayentaStageDetailsCtrl.resolvedControl }}</div>
41
+ </div>
42
+ <div class="row">
43
+ <div class="col-md-4 sm-label-right compact">Canary</div>
44
+ <div class="col-md-8" style="word-wrap: break-word">{{ kayentaStageDetailsCtrl.resolvedExperiment }}</div>
45
+ </div>
46
+ <div class="row" ng-if="stage.context.canaryConfig.scopes[0].step">
47
+ <div class="col-md-4 sm-label-right compact">Step</div>
48
+ <div class="col-md-8" style="word-wrap: break-word">{{stage.context.canaryConfig.scopes[0].step}}</div>
49
+ </div>
50
+ <div class="row" ng-if="stage.context.canaryConfig.scopes[0].startTimeIso">
51
+ <div class="col-md-4 sm-label-right compact">Start Time</div>
52
+ <div class="col-md-8" style="word-wrap: break-word">
53
+ {{ stage.context.canaryConfig.scopes[0].startTimeIso | dateToMillis | timestamp}}
54
+ </div>
55
+ </div>
56
+ <div class="row" ng-if="stage.context.canaryConfig.scopes[0].endTimeIso">
57
+ <div class="col-md-4 sm-label-right compact">End Time</div>
58
+ <div class="col-md-8" style="word-wrap: break-word">
59
+ {{stage.context.canaryConfig.scopes[0].endTimeIso | dateToMillis | timestamp}}
60
+ </div>
61
+ </div>
62
+ </div>
63
+ <div class="col-md-12 canary-config-section">
64
+ <h5>Analysis Config</h5>
65
+ <div class="horizontal-rule"></div>
66
+ <div class="row">
67
+ <div class="col-md-4 sm-label-right compact">Config Name</div>
68
+ <div class="col-md-8" style="word-wrap: break-word">
69
+ <a
70
+ href
71
+ ui-sref="home.applications.application.canary.canaryConfig.configDetail({id: stage.context.canaryConfig.canaryConfigId})"
72
+ >
73
+ {{kayentaStageDetailsCtrl.canaryConfigName}}
74
+ </a>
75
+ </div>
76
+ </div>
77
+ <div class="row">
78
+ <div class="col-md-4 sm-label-right compact">Analysis Type</div>
79
+ <div class="col-md-8" style="word-wrap: break-word">{{stage.context.analysisType | robotToHuman}}</div>
80
+ </div>
81
+ <div class="row" ng-if="stage.context.canaryConfig.lifetimeHours">
82
+ <div class="col-md-4 sm-label-right compact">Duration</div>
83
+ <div class="col-md-6">{{stage.context.canaryConfig.lifetimeHours}} hours</div>
84
+ </div>
85
+ <div class="row">
86
+ <div class="col-md-4 sm-label-right compact">Pass</div>
87
+ <div class="col-md-6">{{stage.context.canaryConfig.scoreThresholds.pass}}</div>
88
+ </div>
89
+ <div class="row">
90
+ <div class="col-md-4 sm-label-right compact">Marginal</div>
91
+ <div class="col-md-6">{{stage.context.canaryConfig.scoreThresholds.marginal}}</div>
92
+ </div>
93
+ <div class="row" ng-if="stage.context.canaryConfig.beginCanaryAnalysisAfterMins">
94
+ <div class="col-md-4 sm-label-right compact">Warmup Period</div>
95
+ <div class="col-md-6">{{stage.context.canaryConfig.beginCanaryAnalysisAfterMins}} minutes</div>
96
+ </div>
97
+ <div class="row" ng-if="stage.context.canaryConfig.canaryAnalysisIntervalMins">
98
+ <div class="col-md-4 sm-label-right compact">Interval</div>
99
+ <div class="col-md-6">{{stage.context.canaryConfig.canaryAnalysisIntervalMins}} minutes</div>
100
+ </div>
101
+ <div class="row" ng-if="stage.context.canaryConfig.baselineAnalysisOffsetInMins">
102
+ <div class="col-md-4 sm-label-right compact">Baseline Offset</div>
103
+ <div class="col-md-6">{{stage.context.canaryConfig.baselineAnalysisOffsetInMins}} minutes</div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+
109
+ <div class="step-section-details" ng-if="detailsSection === 'taskStatus'">
110
+ <div class="row">
111
+ <execution-step-details item="stage"></execution-step-details>
112
+ </div>
113
+ </div>
114
+ </div>
@@ -0,0 +1,6 @@
1
+ .canary-config-section {
2
+ .col-md-6,
3
+ .col-md-8 {
4
+ margin-top: 5px;
5
+ }
6
+ }
@@ -0,0 +1,5 @@
1
+ export const KAYENTA_CANARY = 'kayentaCanary';
2
+ export const RUN_CANARY = 'runCanary';
3
+ export const WAIT = 'wait';
4
+ export const CREATE_SERVER_GROUP = 'createServerGroup';
5
+ export const DEPLOY_CANARY_SERVER_GROUPS = 'deployCanaryServerGroups';