Files
nexus/sreweekly/articles/113/03-travis-ci-status-container-based-linux-precise-infrastructure-emergenc.html
2026-09-12 17:23:01 +08:00

1376 lines
62 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- force IE browsers in compatibility mode to use their most aggressive rendering engine -->
<meta charset="utf-8">
<title>Travis CI Status - Container-based Linux Precise infrastructure emergency maintenance</title>
<meta name="description" content="Travis CI&#39;s Status Page - Container-based Linux Precise infrastructure emergency maintenance.">
<!-- Mobile viewport optimization -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
<!-- Time this page was rendered - http://purl.org/dc/terms/issued -->
<meta name="issued" content="1789096922">
<!-- Mobile IE allows us to activate ClearType technology for smoothing fonts for easy reading -->
<meta http-equiv="cleartype" content="on">
<!-- Atlassian Sans & Mono Fonts -->
<link
rel="preconnect"
href="https://ds-cdn.prod-east.frontend.public.atl-paas.net" />
<link
rel="preload"
href="https://ds-cdn.prod-east.frontend.public.atl-paas.net/assets/fonts/atlassian-sans/v3/AtlassianSans-latin.woff2"
as="font" type="font/woff2" crossorigin />
<link
rel="preload"
href="https://ds-cdn.prod-east.frontend.public.atl-paas.net/assets/font-rules/v5/atlassian-fonts.css"
as="style" crossorigin />
<style>
/* Dynamic Font Stack based on Feature Flag */
/* Using Atlassian Sans & Mono */
:root {
--font-stack-a: "Atlassian Sans", "Helvetica Neue", Helvetica, Arial, Sans-Serif;
--font-stack-b: "Atlassian Mono", "SFMono-Medium", "SF Mono", "Segoe UI Mono", "Roboto Mono", "Ubuntu Mono", Menlo, Consolas, Courier, monospace;
}
</style>
<link rel="shortcut icon" type="image/x-icon" href="//dka575ofm4ao0.cloudfront.net/pages-favicon_logos/original/27/favicon-3a4bd34c-f8d3-468e-ab94-50c3bef48ddc.png" />
<link rel="shortcut icon" href='//dka575ofm4ao0.cloudfront.net/pages-favicon_logos/original/27/favicon-3a4bd34c-f8d3-468e-ab94-50c3bef48ddc.png'>
<link rel="alternate" type="application/atom+xml" href="https://www.traviscistatus.com/history.atom" title="Travis CI Status History - Atom Feed">
<link rel="alternate" type="application/rss+xml" href="https://www.traviscistatus.com/history.rss" title="Travis CI Status History - RSS Feed">
<!-- Canonical Link to ensure that only the custom domain is indexed when present -->
<link rel="canonical" href="https://www.traviscistatus.com/incidents/sxrh0l46czqn">
<meta name="_globalsign-domain-verification" content="y_VzfckMy4iePo5oDJNivyYIjh8LffYa4jzUndm_bZ"/>
<link rel="alternate" type="application/atom+xml" title="ATOM" href="https://www.traviscistatus.com/history.atom" />
<!-- Le styles -->
<link rel="stylesheet" media="screen" href="https://dka575ofm4ao0.cloudfront.net/packs/0.c639e0d4c6263fdfe786.css" />
<link rel="stylesheet" media="all" href="https://dka575ofm4ao0.cloudfront.net/assets/status/status_manifest-3975cac43f498cd81d991d1abc986307c014f9c6ec29a9abfeb3b0882674850a.css" />
<script src="https://dka575ofm4ao0.cloudfront.net/assets/jquery-3.5.1.min-729e416557a365062a8a20f0562f18aa171da57298005d392312670c706c68de.js"></script>
<script>
window.pageColorData = {"blue":"#0068FF","border":"#CCE1FF","body_background":"#fff","font":"#333333","graph":"#0068FF","green":"#05c47a","light_font":"#8794A0","link":"#0068FF","orange":"#FF8C3E","red":"#FF5050","yellow":"#efcc00","no_data":"#b3bac5"};
</script>
<style>
/* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */
body,
.layout-content.status.status-api .section .example-container .example-opener .color-secondary,
.grouped-items-selector,
.layout-content.status.status-full-history .history-nav a.current,
div[id^="subscribe-modal"] .modal-footer,
div[id^="subscribe-modal"],
div[id^="updates-dropdown"] .updates-dropdown-section,
#uptime-tooltip .tooltip-box {
background-color:#fff;
}
#uptime-tooltip .pointer-container .pointer-smaller {
border-bottom-color:#fff;
}
/* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */
body.status,
.color-primary,
.color-primary:hover,
.layout-content.status-index .status-day .update-title.impact-none a,
.layout-content.status-index .status-day .update-title.impact-none a:hover,
.layout-content.status-index .timeframes-container .timeframe.active,
.layout-content.status-full-history .month .incident-container .impact-none,
.layout-content.status.status-index .incidents-list .incident-title.impact-none a,
.incident-history .impact-none,
.layout-content.status .grouped-items-selector.inline .grouped-item.active,
.layout-content.status.status-full-history .history-nav a.current,
.layout-content.status.status-full-history .history-nav a:not(.current):hover,
div[id^="subscribe-modal"] .modal-header .close,
.grouped-item-label,
#uptime-tooltip .tooltip-box .tooltip-content .related-events .related-event a.related-event-link {
color:#333333;
}
.layout-content.status.status-index .components-statuses .component-container .name {
color:#333333;
color:rgba(51,51,51,.8);
}
/* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */
small,
.layout-content.status .table-row .date,
.color-secondary,
.layout-content.status .grouped-items-selector.inline .grouped-item,
.layout-content.status.status-full-history .history-footer .pagination a.disabled,
.layout-content.status.status-full-history .history-nav a,
#uptime-tooltip .tooltip-box .tooltip-content .related-events #related-event-header {
color:#8794A0;
}
/* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */
body.status .layout-content.status .border-color,
hr,
.tooltip-base,
.markdown-display table,
div[id^="subscribe-modal"],
#uptime-tooltip .tooltip-box {
border-color:#CCE1FF;
}
div[id^="subscribe-modal"] .modal-footer,
.markdown-display table td {
border-top-color:#CCE1FF;
}
.markdown-display table td + td, .markdown-display table th + th {
border-left-color:#CCE1FF;
}
div[id^="subscribe-modal"] .modal-header,
#uptime-tooltip .pointer-container .pointer-larger {
border-bottom-color:#CCE1FF;
}
#uptime-tooltip .tooltip-box .outage-field {
/*
Generate the background-color for the outage-field from the css_body_background_color and css_border_color.
For the default background (#ffffff) and default css_border_color (#e0e0e0), use the luminosity of the default background with a magic number to arrive at
the original outage-field background color (#f4f5f7). I used the formula Target Color = Color * alpha + Background * (1 - alpha) to find the magic number of ~0.08.
For darker css_body_background_color, luminosity values are lower so alpha trends toward becoming transparent (thus outage-field background becomes same as css_body_background_color).
*/
background-color: rgba(204,225,255,0.31);
/*
outage-field border-color alpha is inverse to the luminosity of css_body_background_color.
That is to say, with a default white background this border is transparent, but on a black background, it's opaque css_border_color.
*/
border-color: rgba(204,225,255,0.0);
}
/* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */
.layout-content.status.status-index .status-day .update-title.impact-critical a,
.layout-content.status.status-index .status-day .update-title.impact-critical a:hover,
.layout-content.status.status-index .page-status.status-critical,
.layout-content.status.status-index .unresolved-incident.impact-critical .incident-title,
.flat-button.background-red {
background-color:#FF5050;
}
.layout-content.status-index .components-statuses .component-container.status-red:after,
.layout-content.status-full-history .month .incident-container .impact-critical,
.layout-content.status-incident .incident-name.impact-critical,
.layout-content.status.status-index .incidents-list .incident-title.impact-critical a,
.status-red .icon-indicator,
.incident-history .impact-critical,
.components-container .component-inner-container.status-red .component-status,
.components-container .component-inner-container.status-red .icon-indicator {
color:#FF5050;
}
.layout-content.status.status-index .unresolved-incident.impact-critical .updates {
border-color:#FF5050;
}
/* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */
.layout-content.status.status-index .status-day .update-title.impact-major a,
.layout-content.status.status-index .status-day .update-title.impact-major a:hover,
.layout-content.status.status-index .page-status.status-major,
.layout-content.status.status-index .unresolved-incident.impact-major .incident-title {
background-color:#FF8C3E;
}
.layout-content.status-index .components-statuses .component-container.status-orange:after,
.layout-content.status-full-history .month .incident-container .impact-major,
.layout-content.status-incident .incident-name.impact-major,
.layout-content.status.status-index .incidents-list .incident-title.impact-major a,
.status-orange .icon-indicator,
.incident-history .impact-major,
.components-container .component-inner-container.status-orange .component-status,
.components-container .component-inner-container.status-orange .icon-indicator {
color:#FF8C3E;
}
.layout-content.status.status-index .unresolved-incident.impact-major .updates {
border-color:#FF8C3E;
}
/* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */
.layout-content.status.status-index .status-day .update-title.impact-minor a,
.layout-content.status.status-index .status-day .update-title.impact-minor a:hover,
.layout-content.status.status-index .page-status.status-minor,
.layout-content.status.status-index .unresolved-incident.impact-minor .incident-title,
.layout-content.status.status-index .scheduled-incidents-container .tab {
background-color:#efcc00;
}
.layout-content.status-index .components-statuses .component-container.status-yellow:after,
.layout-content.status-full-history .month .incident-container .impact-minor,
.layout-content.status-incident .incident-name.impact-minor,
.layout-content.status.status-index .incidents-list .incident-title.impact-minor a,
.status-yellow .icon-indicator,
.incident-history .impact-minor,
.components-container .component-inner-container.status-yellow .component-status,
.components-container .component-inner-container.status-yellow .icon-indicator,
.layout-content.status.manage-subscriptions .confirmation-infobox .fa {
color:#efcc00;
}
.layout-content.status.status-index .unresolved-incident.impact-minor .updates,
.layout-content.status.status-index .scheduled-incidents-container {
border-color:#efcc00;
}
/* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */
.layout-content.status.status-index .status-day .update-title.impact-maintenance a,
.layout-content.status.status-index .status-day .update-title.impact-maintenance a:hover,
.layout-content.status.status-index .page-status.status-maintenance,
.layout-content.status.status-index .unresolved-incident.impact-maintenance .incident-title,
.layout-content.status.status-index .scheduled-incidents-container .tab {
background-color:#0068FF;
}
.layout-content.status-index .components-statuses .component-container.status-blue:after,
.layout-content.status-full-history .month .incident-container .impact-maintenance,
.layout-content.status-incident .incident-name.impact-maintenance,
.layout-content.status.status-index .incidents-list .incident-title.impact-maintenance a,
.status-blue .icon-indicator,
.incident-history .impact-maintenance,
.components-container .component-inner-container.status-blue .component-status,
.components-container .component-inner-container.status-blue .icon-indicator {
color:#0068FF;
}
.layout-content.status.status-index .unresolved-incident.impact-maintenance .updates,
.layout-content.status.status-index .scheduled-incidents-container {
border-color:#0068FF;
}
/* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */
.layout-content.status.status-index .page-status.status-none {
background-color:#05c47a;
}
.layout-content.status-index .components-statuses .component-container.status-green:after,
.status-green .icon-indicator,
.components-container .component-inner-container.status-green .component-status,
.components-container .component-inner-container.status-green .icon-indicator {
color:#05c47a;
}
/* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */
a,
a:hover,
.layout-content.status-index .page-footer span a:hover,
.layout-content.status-index .timeframes-container .timeframe:not(.active):hover,
.layout-content.status-incident .subheader a:hover {
color:#0068FF;
}
.flat-button,
.masthead .updates-dropdown-container .show-updates-dropdown,
.layout-content.status-full-history .show-filter.open {
background-color:#0068FF;
}
/* CUSTOM COLOR OVERRIDES FOR UPTIME SHOWCASE */
.components-section .components-uptime-link {
color: #8794a0;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .legend-item {
color: #8794a0;
opacity: 1;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .legend-item.light {
color: #8794a0;
opacity: 1;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .spacer {
background: #8794a0;
opacity: 1;
}
</style>
<!-- custom css -->
<!-- polyfills -->
<script crossorigin="anonymous" src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.js"></script>
<!-- Le HTML5 shim -->
<!--[if lt IE 9]>
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- injection for static -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site">
<meta name="twitter:creator">
<meta name="robots" content="noindex,nofollow">
<meta property="og:type" content="article">
<meta property="og:url" content="https://www.traviscistatus.com/incidents/sxrh0l46czqn">
<meta property="og:image" content="https://dka575ofm4ao0.cloudfront.net/assets/logos/favicon-2b86ed00cfa6258307d4a3d0c482fd733c7973f82de213143b24fc062c540367.png">
<meta property="og:title" content="Container-based Linux Precise infrastructure emergency maintenance">
<meta property="og:description">
</head>
<body class="status incident status-none sp-a11y-status-focus-visibility a11y-focus-indicators">
<style>
/* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */ /* BODY BACKGROUND */
body,
.layout-content.status.status-api .section .example-container .example-opener .color-secondary,
.grouped-items-selector,
.layout-content.status.status-full-history .history-nav a.current,
div[id^="subscribe-modal"] .modal-footer,
div[id^="subscribe-modal"],
div[id^="updates-dropdown"] .updates-dropdown-section,
#uptime-tooltip .tooltip-box {
background-color:#fff;
}
#uptime-tooltip .pointer-container .pointer-smaller {
border-bottom-color:#fff;
}
/* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */ /* PRIMARY FONT COLOR */
body.status,
.color-primary,
.color-primary:hover,
.layout-content.status-index .status-day .update-title.impact-none a,
.layout-content.status-index .status-day .update-title.impact-none a:hover,
.layout-content.status-index .timeframes-container .timeframe.active,
.layout-content.status-full-history .month .incident-container .impact-none,
.layout-content.status.status-index .incidents-list .incident-title.impact-none a,
.incident-history .impact-none,
.layout-content.status .grouped-items-selector.inline .grouped-item.active,
.layout-content.status.status-full-history .history-nav a.current,
.layout-content.status.status-full-history .history-nav a:not(.current):hover,
div[id^="subscribe-modal"] .modal-header .close,
.grouped-item-label,
#uptime-tooltip .tooltip-box .tooltip-content .related-events .related-event a.related-event-link {
color:#333333;
}
.layout-content.status.status-index .components-statuses .component-container .name {
color:#333333;
color:rgba(51,51,51,.8);
}
/* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */ /* SECONDARY FONT COLOR */
small,
.layout-content.status .table-row .date,
.color-secondary,
.layout-content.status .grouped-items-selector.inline .grouped-item,
.layout-content.status.status-full-history .history-footer .pagination a.disabled,
.layout-content.status.status-full-history .history-nav a,
#uptime-tooltip .tooltip-box .tooltip-content .related-events #related-event-header {
color:#8794A0;
}
/* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */ /* BORDER COLOR */
body.status .layout-content.status .border-color,
hr,
.tooltip-base,
.markdown-display table,
div[id^="subscribe-modal"],
#uptime-tooltip .tooltip-box {
border-color:#CCE1FF;
}
div[id^="subscribe-modal"] .modal-footer,
.markdown-display table td {
border-top-color:#CCE1FF;
}
.markdown-display table td + td, .markdown-display table th + th {
border-left-color:#CCE1FF;
}
div[id^="subscribe-modal"] .modal-header,
#uptime-tooltip .pointer-container .pointer-larger {
border-bottom-color:#CCE1FF;
}
#uptime-tooltip .tooltip-box .outage-field {
/*
Generate the background-color for the outage-field from the css_body_background_color and css_border_color.
For the default background (#ffffff) and default css_border_color (#e0e0e0), use the luminosity of the default background with a magic number to arrive at
the original outage-field background color (#f4f5f7). I used the formula Target Color = Color * alpha + Background * (1 - alpha) to find the magic number of ~0.08.
For darker css_body_background_color, luminosity values are lower so alpha trends toward becoming transparent (thus outage-field background becomes same as css_body_background_color).
*/
background-color: rgba(204,225,255,0.31);
/*
outage-field border-color alpha is inverse to the luminosity of css_body_background_color.
That is to say, with a default white background this border is transparent, but on a black background, it's opaque css_border_color.
*/
border-color: rgba(204,225,255,0.0);
}
/* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */ /* CSS REDS */
.layout-content.status.status-index .status-day .update-title.impact-critical a,
.layout-content.status.status-index .status-day .update-title.impact-critical a:hover,
.layout-content.status.status-index .page-status.status-critical,
.layout-content.status.status-index .unresolved-incident.impact-critical .incident-title,
.flat-button.background-red {
background-color:#FF5050;
}
.layout-content.status-index .components-statuses .component-container.status-red:after,
.layout-content.status-full-history .month .incident-container .impact-critical,
.layout-content.status-incident .incident-name.impact-critical,
.layout-content.status.status-index .incidents-list .incident-title.impact-critical a,
.status-red .icon-indicator,
.incident-history .impact-critical,
.components-container .component-inner-container.status-red .component-status,
.components-container .component-inner-container.status-red .icon-indicator {
color:#FF5050;
}
.layout-content.status.status-index .unresolved-incident.impact-critical .updates {
border-color:#FF5050;
}
/* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */ /* CSS ORANGES */
.layout-content.status.status-index .status-day .update-title.impact-major a,
.layout-content.status.status-index .status-day .update-title.impact-major a:hover,
.layout-content.status.status-index .page-status.status-major,
.layout-content.status.status-index .unresolved-incident.impact-major .incident-title {
background-color:#FF8C3E;
}
.layout-content.status-index .components-statuses .component-container.status-orange:after,
.layout-content.status-full-history .month .incident-container .impact-major,
.layout-content.status-incident .incident-name.impact-major,
.layout-content.status.status-index .incidents-list .incident-title.impact-major a,
.status-orange .icon-indicator,
.incident-history .impact-major,
.components-container .component-inner-container.status-orange .component-status,
.components-container .component-inner-container.status-orange .icon-indicator {
color:#FF8C3E;
}
.layout-content.status.status-index .unresolved-incident.impact-major .updates {
border-color:#FF8C3E;
}
/* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */ /* CSS YELLOWS */
.layout-content.status.status-index .status-day .update-title.impact-minor a,
.layout-content.status.status-index .status-day .update-title.impact-minor a:hover,
.layout-content.status.status-index .page-status.status-minor,
.layout-content.status.status-index .unresolved-incident.impact-minor .incident-title,
.layout-content.status.status-index .scheduled-incidents-container .tab {
background-color:#efcc00;
}
.layout-content.status-index .components-statuses .component-container.status-yellow:after,
.layout-content.status-full-history .month .incident-container .impact-minor,
.layout-content.status-incident .incident-name.impact-minor,
.layout-content.status.status-index .incidents-list .incident-title.impact-minor a,
.status-yellow .icon-indicator,
.incident-history .impact-minor,
.components-container .component-inner-container.status-yellow .component-status,
.components-container .component-inner-container.status-yellow .icon-indicator,
.layout-content.status.manage-subscriptions .confirmation-infobox .fa {
color:#efcc00;
}
.layout-content.status.status-index .unresolved-incident.impact-minor .updates,
.layout-content.status.status-index .scheduled-incidents-container {
border-color:#efcc00;
}
/* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */ /* CSS BLUES */
.layout-content.status.status-index .status-day .update-title.impact-maintenance a,
.layout-content.status.status-index .status-day .update-title.impact-maintenance a:hover,
.layout-content.status.status-index .page-status.status-maintenance,
.layout-content.status.status-index .unresolved-incident.impact-maintenance .incident-title,
.layout-content.status.status-index .scheduled-incidents-container .tab {
background-color:#0068FF;
}
.layout-content.status-index .components-statuses .component-container.status-blue:after,
.layout-content.status-full-history .month .incident-container .impact-maintenance,
.layout-content.status-incident .incident-name.impact-maintenance,
.layout-content.status.status-index .incidents-list .incident-title.impact-maintenance a,
.status-blue .icon-indicator,
.incident-history .impact-maintenance,
.components-container .component-inner-container.status-blue .component-status,
.components-container .component-inner-container.status-blue .icon-indicator {
color:#0068FF;
}
.layout-content.status.status-index .unresolved-incident.impact-maintenance .updates,
.layout-content.status.status-index .scheduled-incidents-container {
border-color:#0068FF;
}
/* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */ /* CSS GREENS */
.layout-content.status.status-index .page-status.status-none {
background-color:#05c47a;
}
.layout-content.status-index .components-statuses .component-container.status-green:after,
.status-green .icon-indicator,
.components-container .component-inner-container.status-green .component-status,
.components-container .component-inner-container.status-green .icon-indicator {
color:#05c47a;
}
/* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */ /* CSS LINK COLOR */
a,
a:hover,
.layout-content.status-index .page-footer span a:hover,
.layout-content.status-index .timeframes-container .timeframe:not(.active):hover,
.layout-content.status-incident .subheader a:hover {
color:#0068FF;
}
.flat-button,
.masthead .updates-dropdown-container .show-updates-dropdown,
.layout-content.status-full-history .show-filter.open {
background-color:#0068FF;
}
/* CUSTOM COLOR OVERRIDES FOR UPTIME SHOWCASE */
.components-section .components-uptime-link {
color: #8794a0;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .legend-item {
color: #8794a0;
opacity: 1;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .legend-item.light {
color: #8794a0;
opacity: 1;
}
.layout-content.status .shared-partial.uptime-90-days-wrapper .legend .spacer {
background: #8794a0;
opacity: 1;
}
</style>
<div class="layout-content status status-incident">
<div class="container">
<div class="page-title">
<h1 class="color-primary incident-name whitespace-pre-wrap impact-maintenance">Container-based Linux Precise infrastructure emergency maintenance</h1>
<div class="font-largest color-secondary subheader ">
Incident
Report for <a class="color-secondary" href="/">Travis CI</a>
</div>
</div>
<div class="incident-updates-container">
<!-- postmortem if it's published -->
<div class="row update-row">
<h2 class="update-title span3 font-large">
Postmortem
</h2>
<div class="update-container span9">
<div class="update-body font-regular">
<div class="markdown-display">
<p>We strive to provide the most stable and user-friendly CI platform
possible so that you and your teams can focus on shipping amazing open source and commercial software. When any portion of our service is unavailable, we know it can bring your productivity to a screeching halt. As developers building a tool for other developers, we understand firsthand how frustrating and debilitating this can be.</p>
<p>We want to take the time to explain what happened. We recognize that this was a significant disruption to the workflow and productivity of all of our users who rely on us for our Linux container-based builds. This is not at all acceptable to us. We are very sorry that it happened.</p>
<h1>Some background information</h1>
<p>A key component of how Travis CI runs users&#39; builds is a service we call <a href="">worker</a>, which is written in Go. This service is responsible for the <em>execution</em> lifecycle of a build. Those responsibilities include:</p>
<ul>
<li>requesting new <em>compute environment resources</em>, in the form of virtual machines or containers</li>
<li>executing a build script inside the VM/container</li>
<li>transmitting the output of the build script into our logging system, for display and storage</li>
<li>reporting the status (passed, errored, failed, cancelled, etc) of a job when the job has <em>finished</em>.</li>
</ul>
<p>This service is built with multiple <em>backends</em>, which lets us be able to use the same service to run builds in Docker, Google Compute Engine, vSphere for macOS, etc. </p>
<p>For our <a href="">container-based builds</a>, we auto-scale numerous EC2 instances, with each one running the Docker daemon, an instance of our <code>worker</code> service, and a finite number of concurrent jobs, which we refer to as the <code>POOL_SIZE</code>.</p>
<p>In order to provide sufficient and reliable IOPS performance for container-based builds, we use EC2 instances with local SSD storage and a customized <a href="https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/#/configure-direct-lvm-mode-for-production">direct-lvm</a> storage driver configuration for the Docker daemon. We also deploy the <code>worker</code> service inside a Docker container, on each EC2 instance, that is pulled down during the instance creation and startup.</p>
<p>We have traditionally published and pulled all of our Docker images from <a href="">quay.io</a>, but had recently switched to publishing/pulling our <code>worker</code> images from the Docker Hub, in support of plans to deploy the <a href="https://docs.docker.com/registry/recipes/mirror/">Docker registry as a pull through cache</a>, inside our VPC, as the registry only support the Docker Hub as an <em>upstream source</em> for the pull through cache mode.</p>
<p>Additionally, due to the nature of how we provision the auto-scaled EC2 instances which run the builds, because builds are run in a <em>push based</em> fashion, and the manner in which we try to <em>gracefully</em> finish long running builds (travis-ci.com users have a timeout limit of 2 hours.), it is currently a <strong>lengthy process</strong> to fully <em>recycle</em> our fleet of container-based builds hosts.</p>
<h1>So what happened?</h1>
<p><strong>Thursday Feb 2nd</strong>, we started rolling out a new worker version (v2.6.2) on EC2, for <code>sudo: false</code>, container-based builds.</p>
<p><strong>Friday Feb 3rd</strong>, we identified <a href="https://github.com/travis-ci/travis-ci/issues/7264">an issue with jobs being incorrectly marked as <code>failed</code></a> and started the rollback to <code>v2.5.0</code>. We also notified customers affected by this via our support ticketing system. At the time we believed the rollback to be happening successfully, but expected it to take a few hours to be fully completed.</p>
<p><strong>Saturday, Feb 4th</strong>, we discovered that the new instances with worker <code>v2.5.0</code> were not actually making it into <em>service</em> and so builds were still being executed by worker <code>v2.6.2</code>.</p>
<p>The on-call engineer dug into why the <code>v2.5.0</code> instances were failing to make it into <em>service</em>, identified a missing <code>tag</code> for the worker&#39;s image on the Docker Hub, created the missing tag, and then got the rollback working properly.</p>
<p>As mentioned previously, we recently switched <strong>where</strong> we pull the worker Docker images and this change was made with our <code>v2.6.0</code> release of the worker. </p>
<p>Due to some stability issues with <code>v2.6.0</code> and <code>v2.6.1</code>, these versions were not put into production for the container-based infrastructure. It wasn&#39;t until <code>v2.6.2</code> appeared, based on it being stable in our other environments, that we began the upgrade for the container-based builds. </p>
<p>The code change for where we pull the docker images from, was done in one code base, while the publishing and tagging process is done in another code base. We did not foresee needing to rollback the container-based infrastructure to <code>v2.5.0</code>, which was originally only published/tagged on quay.io and not the Docker hub. Publishing it to the Docker hub was done as part of the incident remediation. </p>
<p>We also learned we did not have alerting which included the errors logged when the image pulls were failing to find <code>v2.5.0</code>.</p>
<p><strong>Sunday, Feb 5th</strong>, in order to more quickly eliminate the impact of this issue on our users, we chose to declare emergency maintenance and take more disruptive and aggressive actions that would ensure all the EC2 instances were running <code>v2.5.0</code>. At 00:31 UTC the rollback was fully completed and the incident was marked as <strong>Resolved</strong>.</p>
<h1>Contributing Factors</h1>
<p>The major contributing factors in this outage were</p>
<ul>
<li>A change in how our worker&#39;s docker backend executes build scripts, so that <code>bash</code> is run explicitly with a login shell. This change appears to have effects on how bash handles exit codes, in a manner that we have fully investigated yet. This change was not detected by our staging environment tests and revealed insufficient diversity in how our tests reflect the variety of builds ou users are running.</li>
<li>It currently can take us multiple hours to fully cycle out instances because of the safe guards we have in place to do this <em>recycling</em> in a fashion that does not interrupt longer running (up to 2 hours for travis-ci.com users) jobs.</li>
<li>The recent move to pulling our <code>worker</code> Docker images from the Docker Hub, instead of quay, in support of using local registry caching.</li>
<li>Missing coverage in terms of alerting for the errors which were being logged when the image pulls failed.</li>
</ul>
<h1>Going forward?</h1>
<ol>
<li>As part of the incident resolution, we created additional alerting, so that we know we&#39;ll be notified about similar errors in the future.</li>
<li>We are discussing both incremental and radical ways to change our instance replacement process and tooling, so that we can more quickly deploy <strong>and</strong> rollback future worker versions.</li>
<li>We are looking at how we can better improve the diversity of our tests in staging, to help better catch these kinds of regressions before they impact user builds. </li>
<li>Bigger picture, we discussing the potential for moving to a more agent/pull based process for running each job, which would let us more easily replace the worker version without requiring us to restart user builds.</li>
</ol>
<h1>Summary</h1>
<p>We couldn&#39;t be more sorry about this incident and the impact that the build outages and delays had on you, our users and customers. We always use problems like these as an opportunity for us to improve, and this will be no exception.</p>
<p>We thank you for your continued support of Travis CI, we are working hard to make sure we live up to the trust you&#39;ve placed in us and provide you with an excellent build experience for your open source and private repository builds, as we know that continuous integration and deployment tools we provide you are critical to the productivity of you all.</p>
<p>If you have any questions or concerns that were not addressed in this postmortem, please reach out to us via <a href="mailto:support@travis-ci.com">support@travis-ci.com</a> and we&#39;ll do our best to provide you with the answers to your questions or concerns.</p>
</div>
</div>
<div class="update-timestamp font-small color-secondary">
Posted <span class="ago" data-datetime-unix="1487026614000"></span>Feb <var data-var='date'>13</var>, <var data-var='year'>2017</var> - <var data-var='time'>22:56</var> UTC
</div>
</div>
</div>
<hr>
<!-- incident updates in reverse order -->
<div class="row update-row">
<h2 class="update-title span3 font-large">
Resolved
</h2>
<div class="update-container span9">
<div class="update-body font-regular">
<span class="whitespace-pre-wrap">This incident has been resolved.</span>
</div>
<div class="update-timestamp font-small color-secondary">
Posted <span class="ago" data-datetime-unix="1486341106000"></span>Feb <var data-var='date'>06</var>, <var data-var='year'>2017</var> - <var data-var='time'>00:31</var> UTC
</div>
</div>
</div>
<div class="row update-row">
<h2 class="update-title span3 font-large">
Monitoring
</h2>
<div class="update-container span9">
<div class="update-body font-regular">
<span class="whitespace-pre-wrap">The rollout is nearing completion with capacity surpassing demand. We don't expect any build start delays for the remainder of the maintenance.</span>
</div>
<div class="update-timestamp font-small color-secondary">
Posted <span class="ago" data-datetime-unix="1486340211000"></span>Feb <var data-var='date'>06</var>, <var data-var='year'>2017</var> - <var data-var='time'>00:16</var> UTC
</div>
</div>
</div>
<div class="row update-row">
<h2 class="update-title span3 font-large">
Identified
</h2>
<div class="update-container span9">
<div class="update-body font-regular">
<span class="whitespace-pre-wrap">The container-based Linux infrastructure requires emergency maintenance to ensure all instances are running a known working version of the "worker" component. We had started rolling out a newer version this past Thursday, then began rolling back due to reports of mismatched exit code and job status. Please expect partial capacity behavior similar to times of high load during the maintenance, which we expect will take between 1-2 hours.</span>
</div>
<div class="update-timestamp font-small color-secondary">
Posted <span class="ago" data-datetime-unix="1486331783000"></span>Feb <var data-var='date'>05</var>, <var data-var='year'>2017</var> - <var data-var='time'>21:56</var> UTC
</div>
</div>
</div>
<!-- affected components -->
</div>
<div class="page-footer border-color font-small">
<a href="/"><span style="font-family:arial">&larr;</span> Current Status</a>
<span class="color-secondary powered-by"><a class="color-secondary" target="_blank" rel="noopener noreferrer nofollow" href="https://www.atlassian.com/software/statuspage?utm_campaign=www.traviscistatus.com&amp;utm_content=SP-notifications&amp;utm_medium=powered-by&amp;utm_source=inapp">Powered by Atlassian Statuspage</a></span>
</div>
</div>
</div>
<div class="modal hide fade modal-open-incident-subscribe" id="subscribe-modal-sxrh0l46czqn" style="display: none"
data-js-hook="incident-subscription-modal" role="dialog" aria-labelledby="incident-subscription-dialog-header"
aria-describedby="incident-subscription-dialog-description" aria-modal="true">
<form class="modal-content" id="subscribe-form-sxrh0l46czqn" action="/subscriptions/incident.json" accept-charset="UTF-8" data-remote="true" method="post">
<input type="hidden" name="incident_code" id="incident_code" value="sxrh0l46czqn" autocomplete="off" />
<div class="modal-header">
<a href="#" data-dismiss="modal" class="close" data-js-hook="incident-modal-close" aria-label="close" role="button">×</a>
<h1 id="incident-subscription-dialog-header"> Subscribe to Incident </h1>
</div>
<div class="modal-body">
<p style="margin-bottom:25px" id="incident-subscription-dialog-description">
Subscribe to updates for <strong>Container-based Linux Precise infrastructure emergency maintenance</strong> via email. You'll receive email notifications when incidents are updated.
</p>
<div class="control-group">
<label for="email-sxrh0l46czqn">VIA EMAIL:</label>
<div class="controls">
<input type="text" name="email" id="email-sxrh0l46czqn" data-js-hook="email" class="full-width" />
</div>
</div>
</div>
<div class="modal-footer incident-subscribe">
<!-- tests fail if static width isn't present ¯\_(ツ)_/¯ -->
<button name="button" type="submit" class="flat-button cpt-button incident-subscribe-btn-captcha" id="subscribe-btn-sxrh0l46czqn" onclick="submitCaptchaIncidentSubscribe(event)">Subscribe to Incident</button>
<input type="hidden" name="captcha_error" id="captcha_error" value="false" autocomplete="off" />
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response" value="false" autocomplete="off" />
<div class="terms_and_privacy_information left small"><div class="privacy_policy_information small">By subscribing you agree to our <a target="_blank" rel="noopener" class="accessible-link" href="https://www.ideracorp.com/Legal/PrivacyPolicy">Privacy Policy</a>.</div> This site is protected by reCAPTCHA and the Google <a target="_blank" rel="noopener" class="accessible-link" href="https://policies.google.com/privacy">Privacy Policy</a> and <a target="_blank" rel="noopener" class="accessible-link" data-js-hook="captcha-terms-of-service-link" href="https://policies.google.com/terms">Terms of Service</a> apply.</div>
</div>
</form> </div>
<script>
$(function () {
const phoneNumberInputIncident = $('#phone-number-sxrh0l46czqn');
const phoneCountrySelect = $('#phone-country-sxrh0l46czqn');
const errorDiv = $('#sms-atl-error-sxrh0l46czqn');
const phoneCountryDiv = $('#phone-number-code-sxrh0l46czqn');
if(errorDiv.length){
function checkSelectedCountry() {
const selectedCountry = phoneCountrySelect.val();
const isOtpEnabled = phoneCountryDiv.attr('data-otp-enabled') === 'true';
if(false && selectedCountry === 'sg') { // Replace 'SG' with the actual value representing Singapore in your select tag
phoneNumberInputIncident.prop('disabled', true);
errorDiv.html(`Due to new Singapore government regulations, we're currently not supporting text subscriptions in Singapore.<a href="https://community.atlassian.com/t5/Statuspage-articles/Attention-SMS-notifications-will-be-disabled-on-August-1st-2023/ba-p/2424398" target="_blank"> Learn more.</a> <br> Select another method to subscribe.`);
} else {
phoneNumberInputIncident.prop('readOnly', false);
errorDiv.html('');
if(false){
$('#btn-change-number-incident-sxrh0l46czqn').css('display', 'none');
$('#resend-otp-sxrh0l46czqn').css('display', 'none');
$('#timer-incident-sxrh0l46czqn').css('display', 'none');
if(isOtpEnabled){
$('#otp-container-incident-sxrh0l46czqn').css('display', 'block');
$('#btn-subcriber-send-otp-sxrh0l46czqn').css('display', 'block');
}
else {
$('#otp-container-incident-sxrh0l46czqn').css('display', 'none');
$('#btn-subcriber-send-otp-sxrh0l46czqn').css('display', 'none');
}
}
}
}
phoneCountrySelect.on('change', checkSelectedCountry);
checkSelectedCountry();
}
});
document.addEventListener('DOMContentLoaded', function() {
const dropdowns = document.querySelectorAll('.phone-country-dropdown');
if (dropdowns.length > 0) {
dropdowns.forEach(function(dropdown) {
const dropdownId = dropdown.id;
const incidentCode = dropdownId.split('-').pop();
const wrapperDiv = $('#phone-number-code-' + incidentCode);
function updateOtpEnabledAttribute() {
const selectedOption = dropdown.options[dropdown.selectedIndex];
const otpEnabled = selectedOption.getAttribute('data-otp-enabled');
wrapperDiv.attr('data-otp-enabled', otpEnabled);
}
dropdown.addEventListener('change', updateOtpEnabledAttribute);
updateOtpEnabledAttribute();
});
}
});
var countdownTimers = {};
var phoneNumberInputIncident = $('#phone-number-sxrh0l46czqn');
var RESEND_TIMER = 30;
$(function() {
$('#subscribe-form-sxrh0l46czqn').on('ajax:success', function(e, data, status, xhr){
var $form = $(this);
var formId = $form.attr('id');
var incidentCode = formId.split('-').pop();
$('#btn-change-number-incident-' + incidentCode).css('display', 'block');
if ( $('#btn-change-number-incident-'+ incidentCode).css('display') !== 'none') {
$('#btn-change-number-incident-'+ incidentCode).css('display', 'none');
$('#btn-subcriber-send-otp-'+ incidentCode).css('display', 'block');
$('#otp-container-incident-'+ incidentCode).css('display', 'block');
$('#resend-otp-'+ incidentCode).css('display', 'none');
$('#timer-incident-'+ incidentCode).css('display', 'none');
$('#phone-number-' + incidentCode).prop('readOnly', false);
$('#otp-field-' + incidentCode).val('').prop('disabled', true);
}
if(countdownTimers){
clearInterval(countdownTimers[incidentCode]);
}
});
$('#btn-change-number-incident-sxrh0l46czqn').on('click', function (e) {
var incidentCode = $(this).data('incident-code');
showSendOTP(incidentCode);
return false;
});
$('#btn-subcriber-send-otp-sxrh0l46czqn').on('click', function (e) {
let incidentCode = $(this).data('incident-code');
let phoneNumber = $('#phone-number-' + incidentCode).val();
let countryCode = $('#phone-country-' + incidentCode).val();
let incidentCodeField = $('#incident-code-' + incidentCode).val();
sendOtpRequest(phoneNumber, countryCode, incidentCodeField, function (data, status) {
var messageOptions = (data.type !== undefined && data.type !== null) ? { cssClass: data.type } : {};
HRB.utils.notify(data.text, messageOptions);
e.preventDefault();
if (data.type === 'success') {
showChangeNumber(incidentCode);
}
});
});
$('#resend-otp-sxrh0l46czqn').on('click', function(e) {
let incidentCode = $(this).data('incident-code');
let phoneNumber = $('#phone-number-' + incidentCode).val();
let countryCode = $('#phone-country-' + incidentCode).val();
let incidentCodeField = $('#incident-code-' + incidentCode).val();
sendOtpRequest(phoneNumber, countryCode, incidentCodeField, function (data, status) {
var messageOptions = (data.type !== undefined && data.type !== null) ? { cssClass: data.type } : {};
HRB.utils.notify(data.text, messageOptions);
e.preventDefault();
disableResendIncident(incidentCode);
if (data.type === 'success') {
var display = $('#countdown-incident-' + incidentCode);
startTimerIncident(RESEND_TIMER, display, incidentCode );
}
});
});
})
function sendOtpRequest(phoneNumber, countryCode, incidentCode, onSuccess) {
$.ajax({
type: 'POST',
url: "/subscriptions/new-sms",
data: {
phone_number: phoneNumber,
phone_country: countryCode,
incident_code: incidentCode,
type: 'resend'
},
}).done(onSuccess);
}
function showChangeNumber(incidentCode) {
$('#btn-change-number-incident-' + incidentCode).css('display', 'block');
$('#btn-subcriber-send-otp-' + incidentCode).css('display', 'none');
$('#otp-field-' + incidentCode).val('').prop('disabled', false);
$('#phone-number-' + incidentCode).prop('readOnly', true);
$('#resend-otp-'+ incidentCode).css('display', 'block');
var display = $('#countdown-incident-' + incidentCode);
startTimerIncident(RESEND_TIMER, display, incidentCode)
}
function showSendOTP(incidentCode) {
$('#btn-change-number-incident-' + incidentCode).css('display', 'none');
$('#btn-subcriber-send-otp-' + incidentCode).css('display', 'block');
$('#otp-field-' + incidentCode).val('').prop('disabled', true);
$('#phone-number-' + incidentCode).prop('readOnly', false);
$('#resend-otp-'+ incidentCode).css('display', 'none');
$('#timer-incident-'+ incidentCode).css('display', 'none');
clearInterval(countdownTimers[incidentCode]);
}
function startTimerIncident(duration, display, incidentCode){
var timerIncident = duration, seconds;
clearInterval(countdownTimers[incidentCode]);
countdownTimers[incidentCode] = setInterval(function () {
seconds = parseInt(timerIncident % 60, 10);
display.text(seconds);
$('#timer-incident-'+ incidentCode).css('display', 'block');
if(--timerIncident < 0){
enableResendIncident(incidentCode);
clearInterval(countdownTimers[incidentCode]);
}
}, 1000);
disableResendIncident(incidentCode);
}
function enableResendIncident(incidentCode){
$('#resend-otp-' + incidentCode).css('color', '');
$('#resend-otp-' + incidentCode).css('pointer-events', '');
$('#timer-incident-'+ incidentCode).css('display', 'none');
}
function disableResendIncident(incidentCode){
$('#resend-otp-' + incidentCode).css('color', 'grey');
$('#resend-otp-' + incidentCode).css('pointer-events', 'none');
}
</script>
<script src="https://dka575ofm4ao0.cloudfront.net/assets/status_manifest-9c64d4a1ecaac09c5c1685949fa820bec1cb910c467fa2b0b4a14ed8d82838f8.js"></script>
<div id="cpt-notification-container" role="status" aria-live="polite" aria-atomic="true"></div>
<!-- all of the content_for stuff -->
<script src="https://dka575ofm4ao0.cloudfront.net/assets/register_subscription_form-589b657fec607087fc5c740c568270907310bc4f6aaa20256e70f01b103025ca.js"></script>
<script type="text/javascript">
$(function() {
SP.currentPage.registerSubscriptionForm('email');
SP.currentPage.registerSubscriptionForm('webhook');
});
$(function() {
SP.currentPage.registerSubscriptionForm('sxrh0l46czqn');
});
</script>
<script src="https://dka575ofm4ao0.cloudfront.net/assets/status_common-a86dffb81955091741c3de8bded6ce66ae0c6d8f736b57b580f853d9f892727e.js"></script>
<script>
$(function() { // docks custom footer
var content = $('.layout-content > .container')
, header = $('.custom-header-container')
, footer = $('.custom-footer-container')
if (!footer.length) {
content.addClass('default-spacing');
}
else {
var screenHeight = $(window).outerHeight()
, containerHeight = $('.layout-content > .container').outerHeight(true) || null
, headerHeight = $('.custom-header-container').outerHeight(true) || null
, footerHeight = $('.custom-footer-container').outerHeight(true) || null;
if (screenHeight > containerHeight + footerHeight + headerHeight) {
$('.layout-content > .container').css('padding-bottom', screenHeight - containerHeight - footerHeight - headerHeight);
}
}
});
$(function() {
$(document).on('ajax:complete', '.modal.in', function(e) {
// Close the active modal.
$('.modal.in').modal('hide');
});
});
$(function() {
var timeDifference = function(previous) {
var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;
var now = Date.now();
var elapsedMs = now - previous;
var elapsed = Math.round(elapsedMs/msPerMinute);
if (elapsed === 0) {
return elapsedString(1) + ' minute ago. ';
}
else if (elapsed < 60) {
return elapsedString(elapsed) + ' minute' + plural(elapsed) + ' ago. ';
}
elapsed = Math.round(elapsedMs/msPerHour);
if (elapsed < 24) {
return elapsedString(elapsed) + ' hour' + plural(elapsed) + ' ago. ';
}
elapsed = Math.round(elapsedMs/msPerDay);
if (elapsed < 31) {
return elapsedString(elapsed) + ' day' + plural(elapsed) + ' ago. ';
}
elapsed = Math.round(elapsedMs/msPerMonth);
if (elapsed < 12) {
return elapsedString(elapsed) + ' month' + plural(elapsed) + ' ago. ';
}
elapsed = Math.round(elapsedMs/msPerYear);
return elapsedString(elapsed) + ' year' + plural(elapsed) + ' ago. ';
};
var elapsedString = function(elapsed) {
return '<var data-var="num">' + elapsed + '</var>'
};
var plural = function(x) {
return (x <= 1) ? '' : 's';
};
var relative_dates = $('span.ago');
relative_dates.each(function() {
var $el = $(this);
var time = $el.data("datetime-unix");
$el.html(timeDifference(time));
})
});
</script>
<script>
/** INITIALIZATION **/
var recaptchaIds = {}
// Unfortunately there's no unique selectors on the parent divs that recaptcha adds. The first unique selector
// is the iframe rendered 2 levels deep. So this waits until the iframes are added to the page, then finds
// the parent div and sets the z index so that it'll render above our modals & dropdowns from the start.
function setZIndex(captchaCount, startTime) {
// bail after 10s just in case so we don't do this forever if something whaky happens
if (new Date() - startTime > 10000) {
return;
}
var iframes = document.querySelectorAll('iframe[title="recaptcha challenge"]');
if (iframes.length != captchaCount) {
setTimeout(function() {
setZIndex(captchaCount, startTime);
}, 500);
}
for (var i = 0; i < iframes.length; i++) {
// incident subscribe modal is 1050, so this has to be above that
iframes[i].parentElement.parentElement.style.zIndex = "1100";
}
}
function updateCaptchaIframeTitle(captchaCount, startTime, updates=0) {
if (new Date() - startTime > 10000 || captchaCount === updates) {
return;
}
var iframesWithTitle = document.querySelectorAll('iframe[title="recaptcha challenge expires in two minutes"]');
if (iframesWithTitle.length != captchaCount) {
setTimeout(function() {
updateCaptchaIframeTitle(captchaCount, startTime, iframesWithTitle.length + updates);
}, 500);
}
for (var i = 0; i < iframesWithTitle.length; i++) {
iframesWithTitle[i].title = "recaptcha";
}
}
function addIncidentCaptcha() {
var incidentCaptcha = document.createElement('div');
incidentCaptcha.setAttribute('id', 'subscribe-incident-recaptcha');
incidentCaptcha.setAttribute('class', 'g-recaptcha');
incidentCaptcha.setAttribute('data-sitekey', '6LcZ-b0UAAAAAENi956aWzynTT2ZJ80dGU3F80Op');
incidentCaptcha.setAttribute('data-callback', 'submitIncidentSubscriberSuccess');
incidentCaptcha.setAttribute('data-error-callback', 'submitIncidentSubscriberError');
incidentCaptcha.setAttribute('data-size', 'invisible');
document.body.appendChild(incidentCaptcha);
var incidentCode = document.createElement('input');
incidentCode.setAttribute('type', 'hidden');
incidentCode.setAttribute('id', 'submit_incident_code');
document.body.appendChild(incidentCode);
}
var onloadCallback = function() {
// if there is an incident, then add incident captcha element
if (document.getElementsByClassName('modal-open-incident-subscribe').length > 0) {
addIncidentCaptcha();
}
var captchas = document.getElementsByClassName("g-recaptcha");
for(var i = 0; i < captchas.length; i++) {
var elId = captchas[i].id;
recaptchaIds[elId] = grecaptcha.enterprise.render(elId);
}
setZIndex(captchas.length, new Date());
updateCaptchaIframeTitle(captchas.length, new Date());
}
/** SUBSCRIBE DROPDOWN */
// callbacks for captcha success
function submitNewSubscriber(type, error) {
if (error) document.querySelector('#subscribe-form-' + type + ' #captcha_error').value = 'true';
document.getElementById('subscribe-form-' + type).dispatchEvent(new Event('submit', {bubbles: true, cancelable: true}));
grecaptcha.enterprise.reset(recaptchaIds['subscribe-btn-' + type]);
}
function submitNewEmailSubscriber(token) {
submitNewSubscriber('email');
}
function submitNewSmsSubscriber(token) {
submitNewSubscriber('sms');
}
function submitNewWebhookSubscriber(token) {
submitNewSubscriber('webhook');
}
function submitIncidentSubscriber(token, error) {
var incidentCode = document.getElementById('submit_incident_code').value;
var incidentForm = document.getElementById('subscribe-form-' + incidentCode);
incidentForm.querySelector('input[name="captcha_error"]').value = error;
incidentForm.querySelector('input[name="g-recaptcha-response"]').value = token;
incidentForm.dispatchEvent(new Event('submit', {bubbles: true, cancelable: true}));
grecaptcha.enterprise.reset(recaptchaIds['subscribe-incident-recaptcha']);
}
function submitIncidentSubscriberSuccess(token) {
submitIncidentSubscriber(token, 'false');
}
// callbacks if we get captcha network errors
function emailSubscriberCaptchaError(token) {
submitNewSubscriber('email', true);
}
function smsSubscriberCaptchaError(token) {
submitNewSubscriber('sms', true);
}
function webhookSubscriberCaptchaError(token) {
submitNewSubscriber('webhook', true);
}
function submitIncidentSubscriberError(token) {
submitIncidentSubscriber(token, 'true');
}
// tracking clicks
['email', 'sms', 'webhook'].forEach(function(type) {
var el = document.getElementById('subscribe-btn-' + type);
el && el.addEventListener("click", function() {
$.ajax({
type: "POST",
url: "/subscriptions/track_attempt",
data: {
type: type
}
})
})
})
// form submission success callbacks
$('#subscribe-form-email').on('ajax:success', function(e, data, status, xhr){
if (data.type === 'success') {
SP.currentPage.updatesDropdown.hide();
var emailField = document.getElementById('email');
if (emailField) {
emailField.value = '';
}
}
});
$('#subscribe-form-sms').on('ajax:success', function(e, data, status, xhr){
if (data.type === 'success' && data.otp_flow !== true) {
SP.currentPage.updatesDropdown.hide();
var phoneField = document.getElementById('phone-number');
if (phoneField) {
phoneField.value = '';
}
}
});
$('#subscribe-form-webhook').on('ajax:success', function(e, data, status, xhr){
if (data.type === 'success') {
SP.currentPage.updatesDropdown.hide();
document.getElementById('endpoint-webhooks').value = '';
document.getElementById('email-webhooks').value = '';
}
});
$('a.subscribe').on('click', function() {
document.body.style.overflow = "hidden";
document.body.style.height = "100vh";
});
$('div.modal-open-incident-subscribe').on('hidden', function(){
document.body.style.overflow = "";
document.body.style.height = "";
});
function submitCaptchaIncidentSubscribe(event) {
var incidentCode = event.target.id.split('-')[2];
event.preventDefault();
$.ajax({
type: "POST",
url: "/subscriptions/track_attempt",
data: {
type: 'incident'
}
})
document.getElementById('submit_incident_code').value = incidentCode;
grecaptcha.enterprise.execute(recaptchaIds['subscribe-incident-recaptcha']);
}
</script>
<script src='https://www.recaptcha.net/recaptcha/enterprise.js?onload=onloadCallback&render=explicit' async defer></script>
<script src="https://dka575ofm4ao0.cloudfront.net/packs/common-a84dbe1e1077d0a735c6.chunk.js"></script>
<script src="https://dka575ofm4ao0.cloudfront.net/packs/globals-f12471d5e878ec0f0705.chunk.js"></script>
<script src="https://dka575ofm4ao0.cloudfront.net/packs/runtime-33399279767e29d9b814.js"></script>
<script>
window.addEventListener('load', function () {
const urlParams = new URLSearchParams(window.location.search);
const messageToken = urlParams.get('slack_message_token');
const channelName = escape(urlParams.get('channel_name'));
if(!!messageToken) {
switch(messageToken) {
case 'slack_auth_error':
HRB.utils.notify('The Slack authorization attempt was unsuccessful. Try again.', {cssClass:'error'});
break;
case 'subscribers_disabled_error':
HRB.utils.notify('Slack subscriptions are not enabled on this page.', {cssClass:'error'});
break;
case 'direct_message_channel_error':
HRB.utils.notify('Subscriptions aren’t supported in direct messages. Try subscribing again and choose a channel instead.', {cssClass:'error'});
break
case 'duplicate_error':
HRB.utils.notify("You're already subscribed to get Slack notifications in that channel.", {cssClass:'error'});
break;
case 'duplicate_private_channel_error':
HRB.utils.notify(`You're already subscribed to get Slack notifications in #${channelName}. Invite the @Statuspage app to that channel to start getting status updates.`, {cssClass: 'error'});
break;
case 'default_success':
HRB.utils.notify("You're now subscribed to get Statuspage updates in Slack!", {cssClass:'success'});
break;
case 'private_channel_success':
HRB.utils.notify(`IMPORTANT: Invite the @Statuspage app to your Slack channel #${channelName} to start getting status updates.`, {cssClass:'success'});
break;
}
}
});
</script>
<!-- FOR FLASH NOTICES -->
<!-- FOR ERROR -->
<script>
$(function() {
var $link = $('<span class="color-secondary powered-by"><a class="color-secondary" target="_blank" rel="noopener noreferrer nofollow" href="https://www.atlassian.com/software/statuspage?utm_campaign=www.traviscistatus.com&amp;utm_content=SP-notifications&amp;utm_medium=powered-by&amp;utm_source=inapp">Powered by Atlassian Statuspage</a></span>');
var setPoweredByStyles = function() {
if (!$('.powered-by').length) {
$link.appendTo($('.page-footer'))
}
$('.powered-by').attr('style', 'display: inline !important; visibility:visible !important; opacity: 1 !important; position:static !important; text-indent:0px !important; transform:scale(1) !important');
}
setInterval(setPoweredByStyles, 1000);
});
</script>
</body>
</html>