<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jason Boxman</title>
    <description>The latest articles on Forem by Jason Boxman (@jboxman).</description>
    <link>https://forem.com/jboxman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F976630%2F113a1793-7bd1-4277-8237-742018c19068.png</url>
      <title>Forem: Jason Boxman</title>
      <link>https://forem.com/jboxman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jboxman"/>
    <language>en</language>
    <item>
      <title>Use Prisma with an SQLite DATETIME datatype</title>
      <dc:creator>Jason Boxman</dc:creator>
      <pubDate>Sun, 26 Feb 2023 01:39:50 +0000</pubDate>
      <link>https://forem.com/chingu/use-prisma-with-an-sqlite-datetime-datatype-29o9</link>
      <guid>https://forem.com/chingu/use-prisma-with-an-sqlite-datetime-datatype-29o9</guid>
      <description>&lt;p&gt;tl;dr - For a &lt;code&gt;DATETIME&lt;/code&gt; datatype in SQLite, Prisma reads and writes a Unix epoch time in milliseconds. Depending on your schema, you might be able to read an RFC 3339 or RFC 2822 formatted date from a &lt;code&gt;TEXT&lt;/code&gt; datatype, if the Prisma schema defines the field as &lt;code&gt;DateTime&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQLite and the DATETIME datatype
&lt;/h2&gt;

&lt;p&gt;Per the &lt;a href="https://sqlite.org/quirks.html#no_separate_datetime_datatype"&gt;SQLite documentation&lt;/a&gt;, it does not have a distinct data type for &lt;code&gt;DATETIME&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SQLite has no &lt;code&gt;DATETIME&lt;/code&gt; datatype. Instead, dates and times can be stored in any of these ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As a TEXT string in the ISO-8601 format. Example: '2018-04-02 12:13:46'.&lt;/li&gt;
&lt;li&gt;As an INTEGER number of seconds since 1970 (also known as "unix time").&lt;/li&gt;
&lt;li&gt;As a REAL value that is the fractional Julian day number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The built-in date and time functions of SQLite understand date/times in all of the formats above, and can freely change between them. Which format you use, is entirely up to your application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To account for this, Prisma reads and writes the NUMERIC datatype for its &lt;a href="https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#datetime"&gt;DateTime support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Prisma also supports an SQLite &lt;code&gt;TEXT&lt;/code&gt; datatype, however the format must be in either the RFC 3339 or RFC 2822 formats, which SQLite does not support, instead favoring ISO-8601.&lt;/p&gt;

&lt;p&gt;As it happens, &lt;a href="https://stackoverflow.com/questions/522251/whats-the-difference-between-iso-8601-and-rfc-3339-date-formats"&gt;ISO-8601 is substantially similar to, but not identical to RFC 3339&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pretty much, yes - RFC 3339 is listed as a profile of ISO 8601. Most notably RFC 3339 specifies a complete representation of date and time (only fractional seconds are optional). The RFC also has some small, subtle differences. For example truncated representations of years with only two digits are not allowed -- RFC 3339 requires 4-digit years, and the RFC only allows a period character to be used as the decimal point for fractional seconds. The RFC also allows the "T" to be replaced by a space (or other character), while the standard only allows it to be omitted (and only when there is agreement between all parties using the representation).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While there's a &lt;a href="https://github.com/prisma/prisma/issues/8510"&gt;Prisma issue&lt;/a&gt; about ISO 8601 support, it's been open for years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read a date from a &lt;code&gt;TEXT&lt;/code&gt; datatype
&lt;/h2&gt;

&lt;p&gt;To read dates you must use one of the following formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RFC2822: For example, &lt;code&gt;Tue, 1 Jul 2003 10:52:37 +0200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;RFC3339: For example, &lt;code&gt;1996-12-19T16:39:57-08:00&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can confirm the aforementioned by querying a test table with a column for each of these date formats.&lt;/p&gt;

&lt;p&gt;First, we create a new table that uses the &lt;code&gt;TEXT&lt;/code&gt; datatype:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iso8601&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rfc2822&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rfc3339&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt; &lt;span class="nb"&gt;numeric&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we defined a a Prisma schema that covers the above database schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Test {
  iso8601 DateTime
  rfc2822 DateTime
  rfc3339 DateTime
  unix DateTime @id
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And populate a table with the following test data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqlite&amp;gt; select * from test;
2016-09-01T10:11:12|Tue, 1 Jul 2003 10:52:37 +0200|1996-12-19T16:39:57-08:00|1472782272
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we query the database for each column individually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iso8601&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rfc2822&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rfc3339&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script outputs the following to the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Could not convert value "2016-09-01T10:11:12" of the field `iso8601` to type `DateTime`.
[ { rfc2822: 2003-07-01T08:52:37.000Z } ]
[ { rfc3339: 1996-12-20T00:39:57.000Z } ]
[ { unix: 1970-01-18T01:06:22.272Z } ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is worth noting that, if instead of an existing SQLite database, you start with a new database, Prisma creates a &lt;code&gt;DateTime&lt;/code&gt; schema field as a &lt;code&gt;DATETIME&lt;/code&gt; datatype in SQLite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- CreateTable
CREATE TABLE "Test" (
    "iso8601" DATETIME,
    "rfc2822" DATETIME,
    "rfc3339" DATETIME,
    "unix" DATETIME NOT NULL PRIMARY KEY
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you're probably going to be working with Unix epoch time in milliseconds in many cases. But as the above demonstrates, you &lt;em&gt;can&lt;/em&gt; read dates in either RFC 2822 or RFC 3339 formats if necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write a human readable date to a sidecar column
&lt;/h2&gt;

&lt;p&gt;One approach to writing a humanized date field is with a sidecar column. For example, consider the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sidecar&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;mytable_insert&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt; &lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;mytable&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;sidecar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'unixepoch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'localtime'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;unix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you insert a date with Prisma, a human readable date is inserted as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqlite&amp;gt; INSERT INTO mytable(unix) VALUES(1472782272);
sqlite&amp;gt; SELECT * FROM mytable;
1472782272|2016-09-01 22:11:12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sqlite.org/datatype3.html"&gt;Datatypes In SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/52310300/when-should-one-use-data-type-real-versus-numeric-in-sqlite"&gt;When should one use data type REAL versus NUMERIC in sqlite?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/522251/whats-the-difference-between-iso-8601-and-rfc-3339-date-formats"&gt;What's the difference between ISO 8601 and RFC 3339 Date Formats?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rdrr.io/cran/anytime/man/iso8601.html"&gt;iso8601: Format a Datetime object: ISO 8601, RFC 2822 or RFC 3339&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>prisma</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Add an unread mail count badge to Gmail</title>
      <dc:creator>Jason Boxman</dc:creator>
      <pubDate>Mon, 26 Dec 2022 18:00:37 +0000</pubDate>
      <link>https://forem.com/jboxman/add-an-unread-mail-count-badge-to-gmail-40p</link>
      <guid>https://forem.com/jboxman/add-an-unread-mail-count-badge-to-gmail-40p</guid>
      <description>&lt;p&gt;If you're a Gmail user, but want a native app like experience, you might be using the &lt;em&gt;Create shortcut...&lt;/em&gt; feature available in Chrome and Chrome-based browsers, such as &lt;a href="https://vivaldi.com/download/" rel="noopener noreferrer"&gt;Vivaldi&lt;/a&gt;. This feature opens a specific URL, such as &lt;code&gt;https://gmail.com/&lt;/code&gt;, in a dedicated browser window and provides a separate icon on the dock (MacOS) or taskbar (Windows). But by default there's no badge counter for unread emails.&lt;/p&gt;

&lt;p&gt;Fortunately, it is possible to add a badge by using the experimental &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/setAppBadge" rel="noopener noreferrer"&gt;&lt;code&gt;Navigator.setAppBadge()&lt;/code&gt; API&lt;/a&gt;. This experimental API is available only in Chrome as of this writing. Here I am going to combine the badge API with &lt;a href="https://violentmonkey.github.io/get-it/" rel="noopener noreferrer"&gt;Violentmonkey&lt;/a&gt;, a browser extension that uses the &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions" rel="noopener noreferrer"&gt;WebExtension&lt;/a&gt; APIs.&lt;/p&gt;

&lt;p&gt;If you want to use the script, it is available from a &lt;a href="https://github.com/jboxman/gmail-badge" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; in the &lt;code&gt;gmail-badge/dist/index.user.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Most of the work is performed by a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" rel="noopener noreferrer"&gt;MutationObserver&lt;/a&gt;. The API is wrapped by &lt;code&gt;VM.observe&lt;/code&gt; for convenience, and so we only need to provide a DOM element to monitor for mutations and a callback function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;VM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@violentmonkey/dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;VM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;$el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// https://github.com/developit/Gmail-unread-count-badge/blob/main/src/content.js&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/Inbox&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt; &lt;/span&gt;&lt;span class="se"&gt;\((\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\))?&lt;/span&gt;&lt;span class="sr"&gt; -/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAppBadge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the preceding code, we do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Observe the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; DOM element for any changes.&lt;/li&gt;
&lt;li&gt;React to a change by querying the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; DOM element, extracting the unread message count, and calling &lt;code&gt;navigator.setAppBadge&lt;/code&gt; to set the badge number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using a MutationObserver, we ensure that whenever Google updates the unread message count in the page title, we can update our unread messages icon badge.&lt;/p&gt;

&lt;p&gt;For reference, &lt;code&gt;VM.observe&lt;/code&gt; wraps the &lt;code&gt;MutationObserver.observe&lt;/code&gt; method in the &lt;a href="https://github.com/violentmonkey/vm-dom" rel="noopener noreferrer"&gt;following way&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MutationRecord&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MutationObserver&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;MutationObserverInit&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mutations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;childList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;disconnect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/jboxman/gmail-badge" rel="noopener noreferrer"&gt;source is available on GitHub&lt;/a&gt; and the userscript lives at: &lt;code&gt;gmail-badge/dist/index.user.js&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Troubleshoot app connectivity on OpenShift</title>
      <dc:creator>Jason Boxman</dc:creator>
      <pubDate>Sun, 18 Dec 2022 01:08:07 +0000</pubDate>
      <link>https://forem.com/chingu/troubleshoot-app-connectivity-on-openshift-bad</link>
      <guid>https://forem.com/chingu/troubleshoot-app-connectivity-on-openshift-bad</guid>
      <description>&lt;p&gt;Once your app is ready, you excitedly deploy it to your favorite Kubernetes cluster, such as &lt;a href="https://www.redhat.com/en/technologies/cloud-computing/openshift" rel="noopener noreferrer"&gt;Red Hat OpenShift&lt;/a&gt;. Inputting the URL into the browser, you hit enter and are greeted with...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Application is not available&lt;/p&gt;

&lt;p&gt;The application is currently not serving requests at this endpoint. It may not have been started or is still starting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, that's a disappointment!&lt;/p&gt;

&lt;p&gt;Fortunately, there are some steps that we can take to determine why your app isn't available to the world.&lt;/p&gt;

&lt;p&gt;This post makes a few assumptions about your app's deployment. In particular:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You deployed to a Red Hat OpenShift cluster (although this is mostly applicable to any Kubernetes cluster)&lt;/li&gt;
&lt;li&gt;You created a &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener noreferrer"&gt;service resource&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You created a &lt;a href="https://docs.openshift.com/container-platform/4.11/networking/routes/route-configuration.html" rel="noopener noreferrer"&gt;route resource&lt;/a&gt;, which is specific to OpenShift and provides ingress for HTTP traffic&lt;/li&gt;
&lt;li&gt;You are not using any &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;network policies&lt;/a&gt; within your project&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our app deployment
&lt;/h2&gt;

&lt;p&gt;By way of example, let's troubleshoot the &lt;a href="https://github.com/openshift/origin/blob/master/examples/hello-openshift/README.md" rel="noopener noreferrer"&gt;OpenShift Hello app&lt;/a&gt;, deployed with the following manifest. The key aspects of this manifest are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A deployment that creates two pods that we can load balance traffic between&lt;/li&gt;
&lt;li&gt;A service that manages our service endpoints, the two pods the deployment creates&lt;/li&gt;
&lt;li&gt;A route that load balances incoming HTTP requests to our service endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is essential that the TCP ports are specified correctly. The app uses &lt;code&gt;8080&lt;/code&gt; for HTTP traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-hello&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-hello&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift/hello-openshift:latest&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-hello-svc&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;route.openshift.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Route&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hello-openshift&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ocp&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-hello.apps-crc.testing&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
  &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;termination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;edge&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-hello-svc&lt;/span&gt;
    &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
  &lt;span class="na"&gt;wildcardPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And once executed, we see the following resources in our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc get all -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP             NODE                 NOMINATED NODE   READINESS GATES
pod/openshift-hello-589f9c7749-rhb2p   1/1     Running   0          11h   10.217.0.232   crc-lgph7-master-0   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
pod/openshift-hello-589f9c7749-wbn9l   1/1     Running   0          11h   10.217.0.236   crc-lgph7-master-0   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;

NAME                          TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE   SELECTOR
service/openshift-hello-svc   ClusterIP   10.217.5.72   &amp;lt;none&amp;gt;        8080/TCP   11h   app=ocp

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS        IMAGES                             SELECTOR
deployment.apps/openshift-hello   2/2     2            2           11h   openshift-hello   openshift/hello-openshift:latest   app=ocp

NAME                                         DESIRED   CURRENT   READY   AGE   CONTAINERS        IMAGES                             SELECTOR
replicaset.apps/openshift-hello-589f9c7749   2         2         2       11h   openshift-hello   openshift/hello-openshift:latest   app=ocp,pod-template-hash=589f9c7749

NAME                                       HOST/PORT                          PATH   SERVICES              PORT   TERMINATION   WILDCARD
route.route.openshift.io/hello-openshift   openshift-hello.apps-crc.testing          openshift-hello-svc   8080   edge          None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Our troubleshooting steps
&lt;/h2&gt;

&lt;p&gt;Given the deployment described in the previous section, now we can dive into troubleshooting. We want to check for connectivity issues between each resource. In particular, we're going to look whether the app responds to an HTTP request.&lt;/p&gt;

&lt;p&gt;We're going to answer the following questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the app start successfully?&lt;/li&gt;
&lt;li&gt;Can I connect to an app pod through a port forward?&lt;/li&gt;
&lt;li&gt;Can I connect to the service?&lt;/li&gt;
&lt;li&gt;Can I connect to the route?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all cases, &lt;code&gt;oc&lt;/code&gt; commands are executed within the &lt;code&gt;hello-openshift&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did the app start successfully?
&lt;/h3&gt;

&lt;p&gt;For this, let's look at the logs for our app pods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc get pods -l app=ocp \
  -o jsonpath='{range .items[*]}pods/{.metadata.name}{"\n"}{end}' | \
  xargs -L1 -I% bash -c 'echo % &amp;amp;&amp;amp; oc logs %'
pods/openshift-hello-589f9c7749-rhb2p
serving on 8888
serving on 8080
Servicing request.
Servicing request.
Servicing request.
Servicing request.
Servicing request.
pods/openshift-hello-589f9c7749-wbn9l
serving on 8888
serving on 8080
Servicing request.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the app log includes any errors, these are worth investigating further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I connect to an app pod through a port forward?
&lt;/h3&gt;

&lt;p&gt;For this, we can setup a port forward to an app pod and attempt to connect directly to the application, bypassing our ingress configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc port-forward pods/openshift-hello-589f9c7749-rhb2p 8080
Forwarding from 127.0.0.1:8080 -&amp;gt; 8080
Forwarding from [::1]:8080 -&amp;gt; 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the forward successfully running, in a different terminal window, use the &lt;code&gt;curl&lt;/code&gt; command to test connectivity. (In this example, we're using an &lt;code&gt;http://&lt;/code&gt; URL because our app does not use TLS. Instead, we rely on the OpenShift ingress controller to manage TLS encryption at the &lt;em&gt;edge&lt;/em&gt;. This configuration is referred to as edge terminated.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -v http://localhost:8080/
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: localhost:8080
&amp;gt; User-Agent: curl/7.79.1
&amp;gt; Accept: */*
&amp;gt;
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Date: Wed, 07 Dec 2022 23:17:43 GMT
&amp;lt; Content-Length: 17
&amp;lt; Content-Type: text/plain; charset=utf-8
&amp;lt;
Hello OpenShift!
* Connection #0 to host localhost left intact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our app is reachable through the port forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I connect to the service?
&lt;/h3&gt;

&lt;p&gt;Because our original manifest created a service for this, each app pod with the label &lt;code&gt;app=ocp&lt;/code&gt; is included as an endpoint for the &lt;code&gt;openshift-hello-svc&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;By describing the service, we learn its endpoints: &lt;code&gt;10.217.0.232:8080&lt;/code&gt; and &lt;code&gt;10.217.0.236:8080&lt;/code&gt;. And these endpoints match the IP addresses for our app pods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc describe services/openshift-hello-svc
Name:              openshift-hello-svc
Namespace:         hello-openshift
Labels:            app=ocp
Annotations:       &amp;lt;none&amp;gt;
Selector:          app=ocp
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.217.5.72
IPs:               10.217.5.72
Port:              &amp;lt;unset&amp;gt;  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.217.0.232:8080,10.217.0.236:8080
Session Affinity:  None
Events:            &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or we can look at the endpoint resources in the project. The IP addresses and ports match our &lt;code&gt;openshift-hello-svc&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc get endpoints
NAME                  ENDPOINTS                             AGE
openshift-hello-svc   10.217.0.232:8080,10.217.0.236:8080   12h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this connection test, let's use a &lt;a href="https://github.com/nicolaka/netshoot" rel="noopener noreferrer"&gt;Netshoot&lt;/a&gt; container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot

                    dP            dP                           dP   
                    88            88                           88   
88d888b. .d8888b. d8888P .d8888b. 88d888b. .d8888b. .d8888b. d8888P 
88'  `88 88ooood8   88   Y8ooooo. 88'  `88 88'  `88 88'  `88   88   
88    88 88.  ...   88         88 88    88 88.  .88 88.  .88   88   
dP    dP `88888P'   dP   `88888P' dP    dP `88888P' `88888P'   dP   

Welcome to Netshoot! (github.com/nicolaka/netshoot)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the Netshoot container, let's confirm that we can access the cluster IP of the service, which internally routes the traffic to one of our app pods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -v http://10.217.5.72:8080/ 
*   Trying 10.217.5.72:8080...
* Connected to 10.217.5.72 (10.217.5.72) port 8080 (#0)
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: 10.217.5.72:8080
&amp;gt; User-Agent: curl/7.86.0
&amp;gt; Accept: */*
&amp;gt; 
* Mark bundle as not supporting multiuse
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Date: Fri, 09 Dec 2022 03:39:28 GMT
&amp;lt; Content-Length: 17
&amp;lt; Content-Type: text/plain; charset=utf-8
&amp;lt; 
Hello OpenShift!
* Connection #0 to host 10.217.5.72 left intact
#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit the shell to terminate the Netshoot pod.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I connect to the route?
&lt;/h3&gt;

&lt;p&gt;Finally, let's check whether we can connect to the exposed public route of our app. We can describe the route and confirm what the hostname for our app is: &lt;code&gt;openshift-hello.apps-crc.testing&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc describe routes/hello-openshift
Name:           hello-openshift
Namespace:      hello-openshift
Created:        20 hours ago
Labels:         app=ocp
...
Requested Host:     openshift-hello.apps-crc.testing
               exposed on router default (host router-default.apps-crc.testing) 20 hours ago
Path:           &amp;lt;none&amp;gt;
TLS Termination:    edge
Insecure Policy:    &amp;lt;none&amp;gt;
Endpoint Port:      8080

Service:    openshift-hello-svc
Weight:     100 (100%)
Endpoints:  10.217.0.232:8080, 10.217.0.236:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a browser, we can visit &lt;a href="https://openshift-hello.apps-crc.testing/" rel="noopener noreferrer"&gt;https://openshift-hello.apps-crc.testing/&lt;/a&gt; to see if the app loads. Behind the scenes, the ingress controller is handling the TLS termination and load balancing across app endpoints.&lt;/p&gt;

&lt;p&gt;If we cannot connect successfully, on OpenShift it's &lt;a href="https://stackoverflow.com/a/66563562" rel="noopener noreferrer"&gt;possible to enable logging&lt;/a&gt; on the ingress controller to investigate further.&lt;/p&gt;

&lt;p&gt;As a cluster administrator, edit the ingress controller configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc -n openshift-ingress-operator edit ingresscontrollers/default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the &lt;code&gt;spec.logging.access.*&lt;/code&gt; fields as described in the following YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operator.openshift.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IngressController&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openshift-ingress-operator&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
  &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;access&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Container&lt;/span&gt;
      &lt;span class="na"&gt;httpLogFormat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log_source="haproxy-default" log_type="http" c_ip="%ci" c_port="%cp" req_date="%tr" fe_name_transport="%ft" be_name="%b" server_name="%s" res_time="%TR" tot_wait_q="%Tw" Tc="%Tc" Tr="%Tr" Ta="%Ta" status_code="%ST" bytes_read="%B" bytes_uploaded="%U" captrd_req_cookie="%CC" captrd_res_cookie="%CS" term_state="%tsc" actconn="%ac" feconn="%fc" beconn="%bc" srv_conn="%sc" retries="%rc" srv_queue="%sq" backend_queue="%bq" captrd_req_headers="%hr" captrd_res_headers="%hs" http_request="%r"&lt;/span&gt;
      &lt;span class="na"&gt;logEmptyRequests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Log&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After updating the ingress controller configuration, you can tail the haproxy logs on a router pod. To list the available router pods, run the &lt;code&gt;oc get -n openshift-ingress pods&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ oc logs -f -n openshift-ingress router-default-&amp;lt;id&amp;gt; log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ideally, if your app is accessible on the pods that it is running on and through the service, the problem lies with the ingress configuration. Troubleshooting issues with ingress is beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;For more information on the OpenShift Ingress Operator, see the &lt;a href="https://docs.openshift.com/container-platform/4.11/networking/ingress-operator.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While in these examples, the app is reachable at every step, you can use this approach to reveal whether a connectivity issue prevents you from connecting to your app successfully from your exposed route if you are facing an &lt;em&gt;Application is not available&lt;/em&gt; error page.&lt;/p&gt;

</description>
      <category>github</category>
    </item>
  </channel>
</rss>
