<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="http://lajosveres.com/feed.xml" rel="self" type="application/atom+xml" /><link href="http://lajosveres.com/" rel="alternate" type="text/html" /><updated>2023-06-22T14:29:52+00:00</updated><id>http://lajosveres.com/feed.xml</id><title type="html">Lajos Veres’ page</title><subtitle>Lajos Veres' page</subtitle><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><entry><title type="html">Setting up Devops Weekly newsletter’s archive</title><link href="http://lajosveres.com/devops-weekly-archive/" rel="alternate" type="text/html" title="Setting up Devops Weekly newsletter’s archive" /><published>2021-02-28T00:00:00+00:00</published><updated>2021-02-28T00:00:00+00:00</updated><id>http://lajosveres.com/devops-weekly-archive</id><content type="html" xml:base="http://lajosveres.com/devops-weekly-archive/">&lt;p&gt;I am a subscriber of the &lt;a href=&quot;https://www.devopsweekly.com/&quot;&gt;Devops weekly newsletter&lt;/a&gt;.
I was wondering a few weeks ago whether it had any web-based archive or not.
It would have been more convenient for me to read it on the web than in my mailer.
I asked its author whether it had any public archives, and also if he would mind if I created one.
He confirmed that it had not had and he gave its approval.&lt;/p&gt;

&lt;p&gt;I am a subscriber since 2015, so I have a few hundred issues.
Unfortunately not all of them, but enough to start to build something meaningful.&lt;/p&gt;

&lt;p&gt;My goal was to build something simple based on technologies that I knew or I was relatively confident that they were not going
to need lots of time to learn. I wanted to minimize the costs, but also wanted something stable.&lt;/p&gt;

&lt;p&gt;I used previously &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub pages&lt;/a&gt; with &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;.
GitHub pages offers you free &lt;a href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt;-backed hosting.
So it comes implicitly with version tracking and backups.
Also, anybody can open Pull Requests to a public repository so potentially others could contribute easily later.
I thought the best is to keep the project open anyway so
I registered a new &lt;a href=&quot;https://github.com/devopsweeklyarchive/&quot;&gt;GitHub organisation&lt;/a&gt; and
I created a &lt;a href=&quot;https://github.com/devopsweeklyarchive/devopsweeklyarchive.github.io&quot;&gt;repository&lt;/a&gt; for the project.
GitHub pages automatically recognises if a repository is named as your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;account&amp;gt;.github.io&lt;/code&gt; and it starts to serve its content
under the same domain. &lt;a href=&quot;https://devopsweeklyarchive.github.io&quot;&gt;devopsweeklyarchive.github.io&lt;/a&gt; in our case.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt; is a ruby-based static site generator templating engine.
At least from this project’s point of view, these are its most relevant features.
Also, it has some nice &lt;a href=&quot;https://jekyllthemes.io/free&quot;&gt;free templates&lt;/a&gt;.
I used previously &lt;a href=&quot;https://github.com/mmistakes/minimal-mistakes&quot;&gt;Minimal Mistakes&lt;/a&gt; which is one of the most popular.
It has multiple options for how you can use it in a project, but &lt;a href=&quot;https://github.com/mmistakes/mm-github-pages-starter/generate&quot;&gt;this starter template&lt;/a&gt;
seemed to be the simplest. So I recreated the project repository from this template.&lt;/p&gt;

&lt;p&gt;Configuring Minimal Mistakes is quite &lt;a href=&quot;https://mmistakes.github.io/minimal-mistakes/docs/configuration/&quot;&gt;straight-forward&lt;/a&gt;.
Mainly it means filling its &lt;a href=&quot;https://github.com/devopsweeklyarchive/devopsweeklyarchive.github.io/blob/master/_config.yml&quot;&gt;_config.yml&lt;/a&gt;.
Which is quite well documented.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MM&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;&amp;gt;-&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this means to ignore newlines until &quot;baseurl:&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;Write an awesome description for your new site here. You can edit this&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;line in _config.yml. It will appear in your document head meta (for&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;Google search results) and in your feed.xml site description.&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;twitter_username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;username&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;github_username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;username&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;minimal_mistakes_skin&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Build settings&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kramdown&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;remote_theme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mmistakes/minimal-mistakes&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Outputting&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/:categories/:title/&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;paginate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# amount of posts to show&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;paginate_path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/page:num/&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Also, I had to delete the example posts from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; folder.
Essentially this is all you need to build an empty site.&lt;/p&gt;

&lt;p&gt;To test it locally, you should run this command:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;jekyll serve &lt;span class=&quot;nt&quot;&gt;--watch&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--incremental&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Its output looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Configuration file: /home/vlajos/devopsweeklyarchive.github.io/_config.yml
            Source: /home/vlajos/devopsweeklyarchive.github.io
       Destination: /home/vlajos/devopsweeklyarchive.github.io/_site
 Incremental build: enabled
      Generating... 
      Remote Theme: Using theme mmistakes/minimal-mistakes
       Jekyll Feed: Generating feed &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;posts
                    &lt;span class=&quot;k&quot;&gt;done in &lt;/span&gt;27.228 seconds.
/usr/lib/ruby/vendor_ruby/pathutil.rb:502: warning: Using the last argument as keyword parameters is deprecated
 Auto-regeneration: enabled &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/home/vlajos/devopsweeklyarchive.github.io'&lt;/span&gt;
    Server address: http://127.0.0.1:4000
  Server running... press ctrl-c to stop.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It serves the website on the mentioned url: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://127.0.0.1:4000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My local ruby installation was not in the best shape, but reinstalling the related modules fixed its problems.&lt;/p&gt;

&lt;p&gt;The next step was to convert the mails into &lt;a href=&quot;https://en.wikipedia.org/wiki/Markdown&quot;&gt;Markdown&lt;/a&gt;
files.
I gathered them into a single mailbox first and ended to use a set of scripts:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;split_big_mbox_to_individuals.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As its name says, this one is just splitting the big mailbox to single files/issues with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;formail&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;bigmbox | formail &lt;span class=&quot;nt&quot;&gt;-ds&lt;/span&gt; sh &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;'cat &amp;gt; mails/msg.$FILENO'&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rl&lt;/span&gt; MAILER-DAEMON mails/&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mbox_to_original.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;is stripping down the unneeded bits from the mails.
Most of the mail headers and the mailing list footers are quite pointless on the web.
Also, it names the files based on the issue number extracted from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Subject&lt;/code&gt; line.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;source_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;mails/&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; ^Subject &lt;span class=&quot;nv&quot;&gt;$source_file&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/.*#//g'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/\?=//g'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;destination_file&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;originals/&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;.mail.txt

&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; ^Subject &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; ^Date &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;nv&quot;&gt;$source_file&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$destination_file&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$destination_file&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'1,/^$/ d'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/^--$/,$d'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;nv&quot;&gt;$source_file&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$destination_file&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;original_to_post.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;is responsible for the main Markdown conversion:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Extracts the issue number from the file name.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt;|sed &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/.mail.txt//g'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/.*\///g'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# These ones construct date variables from the Date header.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;Date: &lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt;|sed &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/Date: //g'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--date&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'+%Y-%m-%d'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;sec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--date&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$date&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'+%Y-%m-%dT%H:%M:%S%:z'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# We generate the post name from the issue's date and number:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;../devopsweeklyarchive.github.io/_posts/&lt;span class=&quot;nv&quot;&gt;$day&lt;/span&gt;-&lt;span class=&quot;nv&quot;&gt;$id&lt;/span&gt;.md

&lt;span class=&quot;c&quot;&gt;# The title comes from the subject with escaping the # character.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# The mails were stored originally in quoted-printable encoding,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# so I needed to convert them to plain text.&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    perl &lt;span class=&quot;nt&quot;&gt;-MMIME&lt;/span&gt;::QuotedPrint &lt;span class=&quot;nt&quot;&gt;-pe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'$_=MIME::QuotedPrint::decode($_);'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt;|&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0,/^$/d'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/^$/,$d'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/#/\\#/g'&lt;/span&gt;|&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'\n'&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;' '&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Then we put together the post's header section:&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'---'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;title: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$title&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;date: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sec&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'---'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# And its body: (I have injected some line-breaks and&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# comments to make it a bit more readable.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Those make it syntactically wrong.)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Convert Quoted printable&lt;/span&gt;
perl &lt;span class=&quot;nt&quot;&gt;-MMIME&lt;/span&gt;::QuotedPrint &lt;span class=&quot;nt&quot;&gt;-pe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'$_=MIME::QuotedPrint::decode($_);'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &amp;lt;&lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Remove header section&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; +5|&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Remove standard footer&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'If you received this email directly then'&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Remove double line-breaks before links&lt;/span&gt;
    perl &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/\n\n *(http)/\n$1/g'&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Add explicit line-break before links&lt;/span&gt;
    perl &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/\n(http)/\n&amp;lt;br&amp;gt;$1/g'&lt;/span&gt; |&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Convert links to Markdown links&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'s/\(https\?:\/\/[^ ]*\)/[\1](\1)/g'&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$filename&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With these scripts, I could populate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; folder with the “main” content.
I also added a simple &lt;a href=&quot;https://devopsweeklyarchive.com/about/&quot;&gt;about&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;I uploaded this version since it had the main parts in place already.&lt;/p&gt;

&lt;p&gt;Then I tweaked it a bit more.&lt;/p&gt;

&lt;p&gt;I did not like the long URL &lt;a href=&quot;https://devopsweeklyarchive.github.io/&quot;&gt;https://devopsweeklyarchive.github.io/&lt;/a&gt;,
so I have bought a bit shorter one: &lt;a href=&quot;https://devopsweeklyarchive.com/&quot;&gt;https://devopsweeklyarchive.com/&lt;/a&gt;.
Fortunately, GitHub pages supports these alternative names, just a
&lt;a href=&quot;https://github.com/devopsweeklyarchive/devopsweeklyarchive.github.io/blob/master/CNAME&quot;&gt;CNAME file&lt;/a&gt; has to be provided.&lt;/p&gt;

&lt;p&gt;GitHub pages offers now HTTPS for the domains with alternative names, but I also wanted to add
&lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare&lt;/a&gt; to the picture.
You can say it is overkill, but still, this is a hobby project, I wanted to play a bit with that as well.
Also if you can have &lt;a href=&quot;https://www.cloudflare.com/ddos/&quot;&gt;DDOS protection&lt;/a&gt; and
world-wide &lt;a href=&quot;https://www.cloudflare.com/cdn&quot;&gt;CDN&lt;/a&gt; for free, then why not?
Anyway, it was still the same simplicity to configure it as a few years ago.
Originally I wanted to buy the domain from them as well, but currently, they offer transfer only.
Although &lt;a href=&quot;https://www.cloudflare.com/products/registrar/&quot;&gt;this page&lt;/a&gt; sounds quite attractive.&lt;/p&gt;

&lt;p&gt;And a few more cherries to the top of the cake.&lt;/p&gt;

&lt;p&gt;I wanted to add the basic Google management tools so registered for &lt;a href=&quot;https://analytics.google.com/analytics/web/&quot;&gt;Analytics&lt;/a&gt;
and &lt;a href=&quot;https://search.google.com/search-console/about&quot;&gt;Search Console Tools&lt;/a&gt;.
Connecting them with the Minimal Mistakes template was very simple, just these lines in the
&lt;a href=&quot;https://github.com/devopsweeklyarchive/devopsweeklyarchive.github.io/blob/master/_config.yml#L121&quot;&gt;configuration&lt;/a&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;google_site_verification&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Google&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;verification&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;code&quot;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;analytics&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;google-gtag&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;google&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tracking_id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Analytics&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;account&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ID&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;anonymize_ip&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# default&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I wanted to create a site map as well since the site structure is very basic, but when I &lt;a href=&quot;https://devopsweeklyarchive.com/sitemap.xml&quot;&gt;checked&lt;/a&gt;,
Minimal Mistakes already generated that for me.&lt;/p&gt;

&lt;p&gt;The last piece was to make the site search a bit smarter.
Minimal Mistakes came with a
&lt;a href=&quot;https://mmistakes.github.io/minimal-mistakes/docs/configuration/#lunr-default&quot;&gt;default search engine&lt;/a&gt;:
&lt;a href=&quot;https://lunrjs.com/&quot;&gt;Lunr&lt;/a&gt;,
but it searched only in the first 50 words and it was not very sophisticated.&lt;/p&gt;

&lt;p&gt;So I choose to enable &lt;a href=&quot;https://www.algolia.com/&quot;&gt;Algolia&lt;/a&gt;.
Fortunately, it is fully supported both with &lt;a href=&quot;https://mmistakes.github.io/minimal-mistakes/docs/configuration/#algolia&quot;&gt;Minimal Mistakes&lt;/a&gt;
and &lt;a href=&quot;https://github.com/algolia/jekyll-algolia&quot;&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And then I reached the project’s most puzzling problem.
Why do these 300 pages generate 10000+ objects in Algolia?
I needed the help of Algolia’s support and I had to dig a bit in its internals to understand
that all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags are going to be indexed separately to be able to search them individually.
They describe this process &lt;a href=&quot;https://community.algolia.com/jekyll-algolia/options.html#nodes-to-index&quot;&gt;here&lt;/a&gt;.
Also, the original newsletter was organised to have two line-breaks between the “body” of an article and the source link of the article.
Which meant that Jekyll generated two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; tags from them.
Understanding this took a bit long, but then removing one of the line-breaks was quite simple.&lt;/p&gt;

&lt;p&gt;Another tricky question is how to integrate the indexing step into a static system?
Luckily, they have a &lt;a href=&quot;https://community.algolia.com/jekyll-algolia/github-pages.html#configuring-travis&quot;&gt;nice solution&lt;/a&gt;
to this problem.
You can configure &lt;a href=&quot;https://travis-ci.com/&quot;&gt;Travis&lt;/a&gt; to execute some steps triggered by Git pushes.
Travis is a much smarter &lt;a href=&quot;https://en.wikipedia.org/wiki/Continuous_integration&quot;&gt;CI&lt;/a&gt;
system than this, but to solve this particular problem, we do not need its other capabilities.
Essentially I needed to copy their
&lt;a href=&quot;https://community.algolia.com/jekyll-algolia/github-pages.html#configuring-travis&quot;&gt;example Travis file&lt;/a&gt;
to the
&lt;a href=&quot;https://github.com/devopsweeklyarchive/devopsweeklyarchive.github.io/blob/master/.travis.yml&quot;&gt;new repository&lt;/a&gt;.
So now we have smart typo-resistant search functionality.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;a href=&quot;https://www.devopsweekly.com/&quot;&gt;Devops weekly newsletter&lt;/a&gt; got its
&lt;a href=&quot;https://devopsweeklyarchive.com/&quot;&gt;public archive&lt;/a&gt;.&lt;/p&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="cloudflare" /><category term="devops" /><category term="github" /><category term="jekyll" /><category term="algolia" /><summary type="html">Some details about setting up a static website from scratch.</summary></entry><entry><title type="html">Verifying webserver configuration with some basic tests</title><link href="http://lajosveres.com/testing-webserver-http-headers/" rel="alternate" type="text/html" title="Verifying webserver configuration with some basic tests" /><published>2015-09-16T00:00:00+00:00</published><updated>2015-09-16T00:00:00+00:00</updated><id>http://lajosveres.com/testing-webserver-http-headers</id><content type="html" xml:base="http://lajosveres.com/testing-webserver-http-headers/">&lt;p&gt;We have a project to restructure our webservers, consolidate their configurations, consider using new technologies and implement
best practices where it is possible.
Which means lots of configuration changes.
Our setup is not too complex. We have only a reverse proxy, 2 webservers (one of them is the reverse proxy), a firewall and we use sometimes
a CDN for different contents. Essentially, we serve 4 sites via https, we have a couple of aliases, redirects, rewrite rules, and some
header manipulation for browser-side caching, security, and compression.&lt;/p&gt;

&lt;p&gt;But if we count the different types of requests it is roughly&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4 (virtual hosts) *&lt;/li&gt;
  &lt;li&gt;3 (caching logics) *&lt;/li&gt;
  &lt;li&gt;2 (http redirects, https versions) *&lt;/li&gt;
  &lt;li&gt;4-5 (aspects of the response we want to verify)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a couple of security-related rules.&lt;/p&gt;

&lt;p&gt;So the number is much higher than we would like to test it manually after each configuration change…&lt;/p&gt;

&lt;p&gt;We googled but we could not find any tool which would offer any assertion mechanism for testing HTTP headers.
To be honest, I am a bit surprised, maybe we were not thorough enough.&lt;/p&gt;

&lt;p&gt;Anyway, we decided to implement something basic for ourselves which fulfills our needs and it is not more complex than the minimum.&lt;/p&gt;

&lt;p&gt;Finally, we picked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget&lt;/code&gt; and wrote a couple of trivial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; functions around it.&lt;/p&gt;

&lt;p&gt;The main logic of the test “framework”:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
wget&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LASTRESULT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;/usr/bin/wget &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-check-certificate&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Accept-encoding: gzip&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--max-redirect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;nt&quot;&gt;--header&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Host: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;://&lt;span class=&quot;nv&quot;&gt;$TargetIp&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$3$4&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LASTCALL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;IP:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TargetIp&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;://&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3$4&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
testShouldContain&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LASTRESULT&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; |grep &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LASTCALL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should contain: '&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
testShouldNotContain&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LASTRESULT&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; |grep &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LASTCALL&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should not contain: '&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;'&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So it is around 10 lines and nothing super-fancy. (And uses global variables… Erm. It could be improved.)&lt;/p&gt;

&lt;p&gt;But let’s see it in action:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;TargetIp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;x.y.z.a
&lt;span class=&quot;c&quot;&gt;#export TargetIp=127.0.0.1&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;test_framework.sh
wget_oursite&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  wget https oursite 443 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Host/port related redirects&lt;/span&gt;
wget http www.oursite.com 80 /
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 301 Moved Permanently&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s1&quot;&gt;'Location: https://www.oursite.com/'&lt;/span&gt;
wget https oursite.com 443 /
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 301 Moved Permanently&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s1&quot;&gt;'Location: https://www.oursite.com/'&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Geo-location redirect&lt;/span&gt;
wget_oursite /
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 301 Moved Permanently&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: text/html; charset=UTF-8&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Location: /uk/&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Frame-Options: sameorigin&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Content-Type-Options: nosniff&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Encoding: gzip&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Vary: Accept-Encoding&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Pragma: no-cache&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Last-Modified:&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# A PHP page gzipped, non-cached:&lt;/span&gt;
wget_oursite /uk/
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: text/html; charset=UTF-8&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Encoding: gzip&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Vary: Accept-Encoding&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Set-Cookie: PHPSESSID&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Pragma: no-cache&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Frame-Options: sameorigin&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Content-Type-Options: nosniff&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Expires: .* 198[0-9]&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Last-Modified:&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Static asset, gzippable&lt;/span&gt;
wget_oursite /uploads/app.css
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: text/css&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Last-Modified:&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Cache-Control: max-age=604800&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Expires:&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Encoding: gzip&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Vary: Accept-Encoding&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Pragma: no-cache&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Frame-Options: sameorigin&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Content-Type-Options: nosniff&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Static asset, non gzippable&lt;/span&gt;
wget_oursite /uploads/picture.jpg
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 200 OK&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type: image/jpeg&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Last-Modified:&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Cache-Control: max-age=604800&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;Expires:&quot;&lt;/span&gt;
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;ETag:&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Content-Encoding: gzip&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Vary: Accept-Encoding&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;Pragma: no-cache&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Frame-Options: sameorigin&quot;&lt;/span&gt;
testShouldNotContain &lt;span class=&quot;s2&quot;&gt;&quot;X-Content-Type-Options: nosniff&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# random not-existing&lt;/span&gt;
wget_oursite /not-existing.html
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 404 Not Found&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# server-status&lt;/span&gt;
wget_oursite /server-status
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 403 Forbidden&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# .htaccess&lt;/span&gt;
wget_oursite /.htaccess
testShouldContain &lt;span class=&quot;s2&quot;&gt;&quot;HTTP/1.1 403 Forbidden&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So in around 2 hours, we implemented 107 basic tests to verify our different assets, security, gzip, proxy, and caching-related rules.
Its runtime is around 2 secs.
So our main goal has been achieved. If we change anything in the setup we can verify that with these rules practically instantly.&lt;/p&gt;

&lt;p&gt;Some more random comments:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The responses are cached. (At least until the following one.)&lt;/li&gt;
  &lt;li&gt;The asserts work with regular expressions. (see Expires: check)&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wget&lt;/code&gt; arguments are not 100% trivial so maybe they need some explanation:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-S&lt;/code&gt;: We started with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--debug&lt;/code&gt; but it gave us too many unneeded lines. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-S&lt;/code&gt; displays only the response headers.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-check-certificate&lt;/code&gt;: Unfortunately the server is not yet accessible under its final DNS name, so the url’s host we are connecting to is different than the name in the certificates.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--header=&quot;Accept-encoding: gzip&quot;&lt;/code&gt;: We also want to test compression.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--max-redirect=0&lt;/code&gt;: By default, wget would follow redirects, but in our case, this is not required.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--header=&quot;Host: $2&quot;&lt;/code&gt;: We want to test different virtual hosts and the current DNS settings point to the live system.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$1://$TargetIp:$3$4&lt;/code&gt;: This is only the construction of the URL.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O /dev/null&lt;/code&gt;: Throw away the downloaded content.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;: Merge wget’s standard error into its output.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We could group the asserts by creating higher-level functions. For example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testCacheHeaders&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testGzipped&lt;/code&gt; and so on.
Maybe we will do this later.&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="apache" /><category term="nginx" /><category term="testing" /><category term="http" /><category term="wget" /><category term="https" /><category term="ssl" /><summary type="html">Behind firewalls, load balancers, cdns, reverse proxies and with a not simple webserver configuration, it can quite be hard to verify all the different locations, virtual hosts, aliases, rewrites.</summary></entry><entry><title type="html">Orchestrating a high traffic static page after the last minute</title><link href="http://lajosveres.com/orchestrating-a-high-traffic-page/" rel="alternate" type="text/html" title="Orchestrating a high traffic static page after the last minute" /><published>2015-08-17T00:00:00+00:00</published><updated>2015-08-17T00:00:00+00:00</updated><id>http://lajosveres.com/orchestrating-a-high-traffic-page</id><content type="html" xml:base="http://lajosveres.com/orchestrating-a-high-traffic-page/">&lt;p&gt;Some background:&lt;/p&gt;

&lt;p&gt;We have launched one of our new pages a couple of days ago:
&lt;a href=&quot;https://onlinedoctor.superdrug.com/perceptions-of-perfection/&quot;&gt;Perceptions of Perfection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The backend team was not very involved in the development of this new page because it seemed to be relatively simple and frontend-only.
We knew that it meant to be viral but we had no clue that this would be viral…&lt;/p&gt;

&lt;p&gt;When I arrived Monday morning at the office, they were waiting for me with the news that something was not OK, the whole system was slow.
I checked my emails and saw that there were a couple of New Relic alerts too.&lt;/p&gt;

&lt;p&gt;I logged in to our frontend web server and realised that something was really wrong. Even the ssh connections lagged.
The system load and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vmstat&lt;/code&gt; did not seem to be too bad and there were not any interesting error log entries.
I checked the site. Both our static and generated pages were very slow.
Which meant in this case more than 20-30 seconds of loading time.&lt;/p&gt;

&lt;p&gt;I checked our Geckoboard and saw that the apdex scores were very wrong. I assumed that maybe a bot attempting to place automated
orders or something similar. I checked the server logs whether there were any suspiciously high traffic ip addresses but found none.
My colleagues had let me know that the static pages were also very slow. Which meant the bot assumption was false…&lt;/p&gt;

&lt;p&gt;I continued the investigation with the &lt;a href=&quot;http://httpd.apache.org/docs/2.4/mod/mod_status.html&quot;&gt;server statuses&lt;/a&gt;.
This is one of apache’s most useful and (I think) less-known features.
And we saw our whole system on top of its head…
Most of the processes were in state “G”. It can be translated to “Gracefully finishing”. But what did this mean exactly?
It was not very obvious and it was not 100% sure but it looked like our proxy webserver failed to handle the connections and
dropped them before draining their content properly.
So something had to be wrong a step before the apaches. I realised that this had to be a nginx problem.&lt;/p&gt;

&lt;p&gt;We also noticed that most of our requests were against the same page.
It was a relatively simple AJAX endpoint that was used to display the expected delivery modes for our sites.
After a few minutes thinking I realised that this page’s result changed only once a day…
We could cache it and we could temporarily replace the whole call with the static result.
We chose the second option as a quick fix and one of my colleagues started to implement a proper caching logic.
This was our first victory. The distribution of the apache requests looked instantly closer to normal than before and the site
gained some responsiveness. But it was still far from perfect.&lt;/p&gt;

&lt;p&gt;So we returned to our previous point: the cause of “G”s. I wanted to verify what the situation was around our nginx.
After some typos and troubles, I enabled the &lt;a href=&quot;https://rtcamp.com/tutorials/nginx/status-page/&quot;&gt;equivalent of mod_status&lt;/a&gt;
on our nginx server using this &lt;a href=&quot;http://www.cyberciti.biz/faq/nginx-enable-and-see-current-status-page/&quot;&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I checked the numbers and saw that we had 2000 connections and most of them were waiting… Hmm.
2000 is very close to 2048.
I opened our nginx config and realised that we had only 1 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_process&lt;/code&gt;… Erm. Our server had 4 cpu cores (although virtuals).
And the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker_connections&lt;/code&gt; value was 2048…&lt;/p&gt;

&lt;p&gt;I did a quick nginx tuning, increased the number of worker processes to 8, and enabled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multi_accept&lt;/code&gt;. So with these new values:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-raw&quot; data-lang=&quot;raw&quot;&gt;worker_processes  8;
multi_accept on;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;the whole system suddenly became more responsive. This was our second victory.
Unfortunately, it did not help much longer than the previous one…
We checked it with firebug and the connection seemed to be OK, but the page was still not stable.
It was still lagging and loading very slowly.&lt;/p&gt;

&lt;p&gt;However, the site became a bit quicker but a couple of pages gave &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;502 bad gateway&lt;/code&gt;. Classic. Something was wrong with the apaches.
With removing the block, enabling more nginx throughput, apache became the bottleneck again. But why?
Mod_status gave us the answer. Most of the connections were blocked. A simple apache restart fixed this bad gateway issue.&lt;/p&gt;

&lt;p&gt;The whole system became slightly better but it was still far from optimal.
The nginx stats looked OK right now, but something was even wrong.
My next ideas were tcpdump and netstat. Maybe something was wrong around SYN handling? TCP backlog issue?
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netstat -s&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netstat -taupn |grep -c SYN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I verified quickly but there were only a couple of connections in SYN_RECV. Fortunately &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netstat&lt;/code&gt; gave us more clues.&lt;/p&gt;

&lt;p&gt;The number of waiting connections was high. After some reading, it was obvious that they were only keepalive connections,
anyway decreasing anything which could use resources seemed to be a good idea, so we decreased the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keepalive_timeout&lt;/code&gt; to 45 from 75.
The number decreased as well, but the responsiveness did not change.&lt;/p&gt;

&lt;p&gt;So return back to netstat. Listing, searching, filtering and grouping the netstat results and watching it for a couple of minutes, I
realised that the Send-Q values were high, 30-40 for hundreds of connections.
It looked like something saturated. After a quick google search, I found &lt;a href=&quot;http://www.ex-parrot.com/pdw/iftop/&quot;&gt;iftop&lt;/a&gt;,
a very neat and simple tool to monitor our network
interface’s usage. Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; (disable hostname lookup) it is mostly useless as most of the network tools, but with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; it showed us that
we had 100Mb traffic… Hmm… I wanted to verify whether our interface was 100Mb or maybe 1Gb but I did not find any working tool.
So we assumed it was 100Mb… It was not a 100% safe bet but it seemed to be easier to fix it than verify properly.
(This was the point when I cursed the third time who installed our www machine and forgot to enable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;munin&lt;/code&gt;… and myself for not having fixed this issue yet…)
Anyway, with the knowledge that our network was saturated, I wanted to verify why.&lt;/p&gt;

&lt;p&gt;I checked our apache logs and the page above. I did not believe the results. There were requests with 1.8M result…
I asked the frontend team to optimise those images quickly and they reduced their size to the third of the original.&lt;/p&gt;

&lt;p&gt;In the meantime, I started to bootstrap a rackspace CDN service. It was surprisingly easy.
I had never worked with CDN services before.
In a nutshell, they work as a reverse proxy. You add a name and the “original” server and it starts to serve the original server’s
content on the new name. And you can add rules for caching. It would be perfect if the caching worked…
Anyway, the frontend team changed the URLs quickly to the CDN’s and I started to debug why it was not caching.&lt;/p&gt;

&lt;p&gt;With adding a simple GET parameter to an image request it was easy to separate
the test requests from the other storming traffic. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Wget&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--debug&lt;/code&gt; showed us the certificate… Hmm, Akamai… Useful information.
I tried different caching rules but I got the same results.
None of them helped. Finally, I added a rule to cache everything for a day.
(Later I realised that the default cache settings were perfect. Images are still cached for a day.)
I compared the requests, assuming maybe the CDN’s caching behaviour was influenced somehow by the original request’s cache controlling
headers.
There were two interesting things: the original requests could be cached for 1 week and there was a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vary: Accept-Encoding&lt;/code&gt;.
What was this?
As a conclusion, it was obvious that the caching rule cannot be inherited by the original server’s rules.
1 week could not be interpreted as nothing.
But what is this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vary:&lt;/code&gt; ?
I remembered it should help cache servers to differentiate between requests and cache them separately based on the headers defined by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vary&lt;/code&gt;.
This should not have been a problem.
But why it was there? Anyway, simplicity sounded better and because this was our only remaining clue, I googled for it.
&lt;a href=&quot;https://www.drupal.org/node/2213429&quot;&gt;Success&lt;/a&gt;! Or at least one more hint.
Akamai did not like that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vary:&lt;/code&gt; header in other cases. Maybe we had the same problem.
Anyway, we did not need these headers for our images. They could not be compressed anyway.
But our whole directory had this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vary:&lt;/code&gt; header. I narrowed down its scope with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;location&lt;/code&gt; to match only to the javascript and css files.
And finally, the CDN started to serve clients from its cache and traffic dropped from 100Mbit to 5-8Mbit on the origin server.
The site became fully responsive. We had few thousands of visitors at the same time.
Happy End.&lt;/p&gt;

&lt;p&gt;Until the end of the day, we had 120k shares, 9k likes, and 4k tweets.&lt;/p&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="cdn" /><category term="akamai" /><category term="rackspace" /><category term="nginx" /><category term="iftop" /><category term="apache" /><category term="netstat" /><category term="newrelic" /><category term="geckoboard" /><category term="apdex" /><category term="postmortem" /><summary type="html">A presumably calm Monday with some trouble and network debugging.</summary></entry><entry><title type="html">Testing an alert was more complex than it seemed to be…</title><link href="http://lajosveres.com/angular-timeout-vs-interval/" rel="alternate" type="text/html" title="Testing an alert was more complex than it seemed to be…" /><published>2015-07-21T00:00:00+00:00</published><updated>2015-07-21T00:00:00+00:00</updated><id>http://lajosveres.com/angular-timeout-vs-interval</id><content type="html" xml:base="http://lajosveres.com/angular-timeout-vs-interval/">&lt;p&gt;Currently, we are working on a big refactoring project. We are rewriting a big chunk of legacy javascript with Angular.js.
We also write end-to-end tests with protractor to keep our system more robust.&lt;/p&gt;

&lt;p&gt;My colleagues used UI Bootstrap module’s &lt;a href=&quot;https://angular-ui.github.io/bootstrap/#/alert&quot; target=&quot;_blank&quot;&gt;alert&lt;/a&gt;
function to display different things to our clients.
And they wanted to test if the alert is displayed properly.&lt;/p&gt;

&lt;p&gt;The first attempts returned with a strange error message:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;37.099&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;WARN&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;thrown&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;openqa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;selenium&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;TimeoutException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;asynchronous&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;was&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;received&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;seconds&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Essentially, the test clicked a button and wanted to check if there is an alert or not.
It was very strange that - based on the logs - the click happened properly, but if we removed anything after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;click()&lt;/code&gt; action the timeout remained still there.
It took a bit of time to understand what happened in the background.&lt;/p&gt;

&lt;p&gt;Unfortunately the alert’s internal dismiss functionality has been implemented with Angular’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$timeout&lt;/code&gt; service which increments
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$browser&lt;/code&gt;’s outstanding request count and protractor with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignoreSynchronization=false&lt;/code&gt; waits correctly until there are any outstanding requests.
This means if you do not disable the synchronization, you cannot see the opened alert, because protractor suspends execution until it is visible.&lt;/p&gt;

&lt;p&gt;So in our case, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;click()&lt;/code&gt; opened the alert, which started the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$timeout&lt;/code&gt;, and protractor suspended the execution and then it timed out.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;https://github.com/angular/protractor/issues/169&quot; target=&quot;_blank&quot;&gt;link&lt;/a&gt;
helped us a lot to understand the situation:&lt;/p&gt;

&lt;p&gt;If you want to test any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$timeout&lt;/code&gt; based functionality you have to turn on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignoreSynchronization&lt;/code&gt; which is basically a hack for non-Angular apps.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/angular/angular.js/commit/2b5ce84fca7b41fca24707e163ec6af84bc12e83&quot; target=&quot;_blank&quot;&gt;There&lt;/a&gt;
they suggest to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$interval&lt;/code&gt; because it fits better to this case.&lt;/p&gt;

&lt;p&gt;As a work around currently we turned on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ignoreSynchronization&lt;/code&gt;, but for a proper solution we implemented the suggested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$interval&lt;/code&gt; transition
and sent a &lt;a href=&quot;https://github.com/angular-ui/bootstrap/pull/3982&quot; target=&quot;_blank&quot;&gt;Pull Request&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It seems that there are different opinions regarding this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$timeout&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$interval&lt;/code&gt; debate. (See the comments in the PR.)&lt;/p&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="angular.js" /><category term="javascript" /><category term="$timeout" /><category term="$interval" /><category term="protractor" /><category term="testing" /><summary type="html">Alternative for $timeout to make protractor testing a bit easier.</summary></entry><entry><title type="html">Jenkins, Browserstack integration: Link jenkins builds with Browserstack sessions</title><link href="http://lajosveres.com/add-browserstack-session-link-to-jenkins/" rel="alternate" type="text/html" title="Jenkins, Browserstack integration: Link jenkins builds with Browserstack sessions" /><published>2015-07-02T00:00:00+00:00</published><updated>2015-07-02T00:00:00+00:00</updated><id>http://lajosveres.com/add-browserstack-session-link-to-jenkins</id><content type="html" xml:base="http://lajosveres.com/add-browserstack-session-link-to-jenkins/">&lt;p&gt;We have a couple of Jenkins tasks for running tests with Browserstack.
One suite contains 5-6 tests and 4 platforms configured with Jenkins’ matrix plugin.
(We plan to increase both dimensions.)&lt;/p&gt;

&lt;p&gt;If any of the tests fail and we want to investigate what happened we have to open a Browserstack window, navigate to the
given build page and find on the page the given session.
This can be painful if the suite is still running because Browserstack is continuously refreshing the page.
When the tests are done at least the page is stable, so you can search with the browser’s find function.
If the test had not run amongst the first ten of the given suite you have to click one more.&lt;/p&gt;

&lt;p&gt;This is not so bad if you do it once or twice a day. Approximately 30 seconds and 4-5 clicks.
But if you develop intensively and the tests are not stable yet this could take 10% of your time maybe even more.&lt;/p&gt;

&lt;p&gt;Therefore we wanted to automate somehow this process. It did not seem to be too complex,
Jenkins already communicates with Browserstack, though it does indirectly, but maybe we could send back the
session’s link to it.&lt;/p&gt;

&lt;p&gt;We asked Browserstack’s support. They told us that we could access the remote session’s id and
with that id, we could also get the session’s url using Browserstack’s REST API.&lt;/p&gt;

&lt;p&gt;Getting the webdriver session’s data:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;    &lt;span class=&quot;nx&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;// All session data&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;// The webdriver session id&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And we can call the REST API using this id on the end-point:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://www.browserstack.com/automate/sessions/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Unfortunately, this second link is protected with HTTP-Auth.
But the expected login/password here is not the API credentials, but the web user’s one.
So we have to allow Jenkins to use somehow one of our web user’s credentials.&lt;/p&gt;

&lt;p&gt;The final code for this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;browser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;session_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;www.browserstack.com&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/automate/sessions/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;auth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;login:password&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;utf8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;automation_session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;browser_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Browserstack session url: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sessionUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In our case, we displayed the capabilities as well like in the first snippet and we have an exception when we test locally
instead of Browserstack and when the password is accessed somehow differently.
So it is a bit more complex but this is the main concept.&lt;/p&gt;

&lt;p&gt;You can place this snippet anywhere in your tests. If you want to run it for every test probably the best place is a centralised code.
We added this to our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protractor.conf&lt;/code&gt;’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onprepare&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;After implementing this we have got a line in our logs like this:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Browserstack session url: https://www.browserstack.com/automate/builds/123.../sessions/456...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When we started to develop this function we did not know yet how we could use this link in Jenkins, so originally this was intended to be only a temporary verification step.
Fortunately Jenkins has a &lt;a href=&quot;https://wiki.jenkins-ci.org/display/JENKINS/Description+Setter+Plugin&quot; target=&quot;_blank&quot;&gt;Description Setter Plugin&lt;/a&gt;
which can run regular expression against the build log and it can use their results customise the given build’s description.&lt;/p&gt;

&lt;p&gt;You have to install the plugin first with these steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Manage Jenkins&lt;/li&gt;
  &lt;li&gt;Manage Plugins&lt;/li&gt;
  &lt;li&gt;Available&lt;/li&gt;
  &lt;li&gt;Description setter plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you have to configure it for all the tasks where you want to use it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the task’s configuration page.&lt;/li&gt;
  &lt;li&gt;Scroll down to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Add post-build action&lt;/code&gt;’s and add the plugin’s call.&lt;/li&gt;
  &lt;li&gt;We use with the following settings:
    &lt;ul&gt;
      &lt;li&gt;Regular expression: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Browserstack session url: (.*)&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Description: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a href=&quot;\1&quot;&amp;gt;Browserstack session&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Regular expression for failed builds: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Browserstack session url: (.*)&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Description for failed builds: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a href=&quot;\1&quot;&amp;gt;Browserstack session&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The last two items appear when you open the advanced options. Without them, this feature is pretty useless for debugging…
We tried to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target=&quot;_blank&quot;&lt;/code&gt;, but it was stripped out by Jenkins. Probably it could be added somehow…&lt;/p&gt;

&lt;p&gt;And the result looks like this in the history:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;#661 Jul 02, 2015 2:09 PM
Browserstack session&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="protractor" /><category term="Browserstack" /><category term="node.js" /><category term="jenkins" /><summary type="html">Linking Browserstack session's in Jenkins' build page using the Description setter plugin and a bit javascript</summary></entry><entry><title type="html">Save our day with ghostscript to concatenate PDF files</title><link href="http://lajosveres.com/ghostscript-pdf-concatenate/" rel="alternate" type="text/html" title="Save our day with ghostscript to concatenate PDF files" /><published>2015-06-16T00:00:00+00:00</published><updated>2015-06-16T00:00:00+00:00</updated><id>http://lajosveres.com/ghostscript-pdf-concatenate</id><content type="html" xml:base="http://lajosveres.com/ghostscript-pdf-concatenate/">&lt;p&gt;This issue started without any extra notice.
Customer service notified us that labels were not uploaded.
Essentially, we receive the labels from our logistic partners, we print them out and apply them to the parcels we send out.
And there is also a small step in the system where we concatenate the label batches to bigger files to make
the printing easier for our colleagues.
Both the input and the output files are simple PDFs.&lt;/p&gt;

&lt;p&gt;When we checked our logs we noticed that something had been changed with the original labels.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-php&quot; data-lang=&quot;php&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mo&quot;&gt;06&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2015&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;333.333.333.333&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PHP&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Fatal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Uncaught&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exception&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'Exception'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'This document (/tmp/label_55a780e22172f.pdf) probably uses a compression technique which is not supported by the free parser shipped with FPDI. (See https://www.setasign.com/fpdi-pdf-parser for more details)'&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;
&lt;span class=&quot;mf&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;FPDI&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.5.2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pdf_parser&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;329&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Stack&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#0 ...lib/FPDI-1.5.2/pdf_parser.php(202): pdf_parser-&amp;gt;_readXref(Array, 87345)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#1 ...lib/FPDI-1.5.2/fpdi_pdf_parser.php(71): pdf_parser-&amp;gt;__construct('/tmp/...')&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#2 ...lib/FPDI-1.5.2/fpdi.php(128): fpdi_pdf_parser-&amp;gt;__construct('/tmp/..')&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#3 ...lib/FPDI-1.5.2/fpdi.php(108): FPDI-&amp;gt;_getPdfParser('/tmp/...')&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#4 ...php/utils/concatpdf.class.php(37): FPDI-&amp;gt;setSourceFile('/tmp/...')&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#5 ...php/utils/shipping/provider in ...lib/FPDI-1.5.2/pdf_parser.php on line 329&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The error message was quite clear: the input PDF files’ format had been changed.
We could verify it easily:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;file label_55a780e22172f.pdf
label_55a780e22172f.pdf: PDF document, version 1.7&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We checked the link the error message mentioned…
They have a commercial add-on to open PDF files higher than 1.4.&lt;/p&gt;

&lt;p&gt;Fortunately, I played with ghostscript a few days ago and remembered that it could manipulate PDF files efficiently.
And Google gave us useful examples on stackoverflow.&lt;/p&gt;

&lt;p&gt;We implemented the new code in less than half an hour. The main logic was very simple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;gs &lt;span class=&quot;nt&quot;&gt;-dBATCH&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-dNOPAUSE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sDEVICE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pdfwrite &lt;span class=&quot;nt&quot;&gt;-sOutputFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$outfile&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$inputfiles&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We gave it a try and it worked.
We tried it with longer files and we noticed that a few labels had changed their orientation.
2-3 of 60 labels were landscape instead of portrait, though all the originals were portrait.
And the problem appeared consistent with the same files.&lt;/p&gt;

&lt;p&gt;We started to play with the switches… Ghostscript had much more options than we expected and its documentation is not really
Google-friendly, but we found finally the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AutoRotatePages&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Our first attempt was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-dAutoRotatePages=/All&lt;/code&gt; setting, but this uses some kind of majority decision for rotating the pages…
We tried this with only one label which had been rotated unintentionally.
It did not work as we could expect.
It seemed that something like this had been enabled on page level.
At least with this given file ghostscript did the same rotation with or without the flag.&lt;/p&gt;

&lt;p&gt;Fortunately, our next attempt was successful.
We added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-dAutoRotatePages=/None&lt;/code&gt;
Which worked properly for both the big batches and the single files.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;gs &lt;span class=&quot;nt&quot;&gt;-dAutoRotatePages&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/None &lt;span class=&quot;nt&quot;&gt;-dBATCH&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-dNOPAUSE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sDEVICE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pdfwrite &lt;span class=&quot;nt&quot;&gt;-sOutputFile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$outfile&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$inputfiles&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A 27 years old software saved our day.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://ghostscript.com/doc/current/History1.htm#Version1.0&quot; target=&quot;_blank&quot;&gt;Link to its version 1.n History&lt;/a&gt;&lt;/p&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="pdf" /><category term="ghostscript" /><category term="fpdi" /><category term="concatenate" /><category term="orientation" /><category term="rotate" /><summary type="html">Using ghostscript to manipulate PDF files.</summary></entry><entry><title type="html">A lucky shot with strace to optimise database</title><link href="http://lajosveres.com/how-to-use-strace-for-db-optimisation/" rel="alternate" type="text/html" title="A lucky shot with strace to optimise database" /><published>2015-05-14T00:00:00+00:00</published><updated>2015-05-14T00:00:00+00:00</updated><id>http://lajosveres.com/how-to-use-strace-for-db-optimisation</id><content type="html" xml:base="http://lajosveres.com/how-to-use-strace-for-db-optimisation/">&lt;p&gt;I received a task to improve our shipment tracking system.
A part of the task was to poll more frequently our partner site’s remote statuses.
Essentially, I had to change the two times a day cadence to 2 times an hour.
We already had a cronjob to manage this task, but I was not very familiar with this sub-system.&lt;/p&gt;

&lt;p&gt;I hoped a bit that maybe it is enough to reconfigure the job’s crontab entry.
Anyway, I wanted to play safely so checked first how long the given tasks run.
The result was really sad. The task worked on only four day’s data but ran for 75 minutes.
The math was simple 75 minutes was too much.&lt;/p&gt;

&lt;p&gt;As far as I knew the job polls only the remote systems and saves the result in the database.&lt;/p&gt;

&lt;p&gt;We did not have too many profiling tools installed on the related machine, so I chose to use the simplest available tool:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I ran the job in one terminal and started a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; in another.&lt;/p&gt;

&lt;p&gt;The result was surpisingly conclusive, the character flow stopped for seconds at the following lines:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\17\1\0\0\3\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;            select&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;275&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;275&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Strace’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-s&lt;/code&gt; switch helped to find the guilty query exactly.
It was a very simple one:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sql&quot; data-lang=&quot;sql&quot;&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remote_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The table received its well-deserved index in minutes and the next run of the job was only 14 minutes.&lt;/p&gt;</content><author><name>Lajos Veres</name><email>lajosveres@lavela.hu</email></author><category term="strace" /><category term="optimisation" /><category term="index" /><category term="mysql" /><summary type="html">How can you get useful insights about running processes.</summary></entry></feed>