Migrate to jekyll for static gen
This commit is contained in:
parent
8280d4fcab
commit
0cc7d7fe42
27 changed files with 1424 additions and 100 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -1,2 +1,7 @@
|
|||
build/*
|
||||
blog/*
|
||||
_site
|
||||
.sass-cache
|
||||
.jekyll-cache
|
||||
.jekyll-metadata
|
||||
vendor
|
||||
|
||||
old
|
||||
|
|
1
.ruby-version
Normal file
1
.ruby-version
Normal file
|
@ -0,0 +1 @@
|
|||
3.1.2
|
25
404.html
Normal file
25
404.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
permalink: /404.html
|
||||
layout: default
|
||||
---
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
.container {
|
||||
margin: 10px auto;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
margin: 30px 0;
|
||||
font-size: 4em;
|
||||
line-height: 1;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container">
|
||||
<h1>404</h1>
|
||||
|
||||
<p><strong>Page not found :(</strong></p>
|
||||
<p>The requested page could not be found.</p>
|
||||
</div>
|
44
Gemfile
Normal file
44
Gemfile
Normal file
|
@ -0,0 +1,44 @@
|
|||
source "https://rubygems.org"
|
||||
# Hello! This is where you manage which Jekyll version is used to run.
|
||||
# When you want to use a different version, change it below, save the
|
||||
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
||||
#
|
||||
# bundle exec jekyll serve
|
||||
#
|
||||
# This will help ensure the proper Jekyll version is running.
|
||||
# Happy Jekylling!
|
||||
gem "jekyll", "~> 4.3.2"
|
||||
# This is the default theme for new Jekyll sites. You may change this to anything you like.
|
||||
gem "minima", "~> 2.5"
|
||||
gem "no-style-please", git: "https://github.com/atomaka/no-style-please", branch: "atomaka"
|
||||
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
|
||||
# uncomment the line below. To upgrade, run `bundle update github-pages`.
|
||||
# gem "github-pages", group: :jekyll_plugins
|
||||
# If you have any plugins, put them here!
|
||||
group :jekyll_plugins do
|
||||
gem "jekyll-feed", "~> 0.12"
|
||||
gem "jekyll-gist"
|
||||
gem "jekyll-tidy"
|
||||
end
|
||||
|
||||
group :development do
|
||||
# import for medium
|
||||
gem "jekyll-import", git: "https://github.com/jekyll/jekyll-import"
|
||||
gem "mdl"
|
||||
gem "rss"
|
||||
gem "safe_yaml"
|
||||
end
|
||||
|
||||
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||
# and associated library.
|
||||
platforms :mingw, :x64_mingw, :mswin, :jruby do
|
||||
gem "tzinfo", ">= 1", "< 3"
|
||||
gem "tzinfo-data"
|
||||
end
|
||||
|
||||
# Performance-booster for watching directories on Windows
|
||||
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
|
||||
|
||||
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
|
||||
# do not have a Java counterpart.
|
||||
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
152
Gemfile.lock
Normal file
152
Gemfile.lock
Normal file
|
@ -0,0 +1,152 @@
|
|||
GIT
|
||||
remote: https://github.com/atomaka/no-style-please
|
||||
revision: e281fa0bbecc82e84fe61be9a92baad5a1a46763
|
||||
branch: atomaka
|
||||
specs:
|
||||
no-style-please (0.4.7)
|
||||
jekyll
|
||||
jekyll-feed
|
||||
jekyll-seo-tag
|
||||
|
||||
GIT
|
||||
remote: https://github.com/jekyll/jekyll-import
|
||||
revision: 6499317a81aeda119b6ceefb37ab81c9f6219659
|
||||
specs:
|
||||
jekyll-import (0.21.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
nokogiri (~> 1.0)
|
||||
reverse_markdown (~> 2.1)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
chef-utils (18.1.0)
|
||||
concurrent-ruby
|
||||
colorator (1.1.0)
|
||||
concurrent-ruby (1.2.0)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0)
|
||||
eventmachine (1.2.7)
|
||||
faraday (2.7.4)
|
||||
faraday-net_http (>= 2.0, < 3.1)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-net_http (3.0.2)
|
||||
ffi (1.15.5)
|
||||
forwardable-extended (2.6.0)
|
||||
google-protobuf (3.21.12-x86_64-linux)
|
||||
htmlbeautifier (1.4.2)
|
||||
htmlcompressor (0.4.0)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (4.3.2)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 1.0)
|
||||
jekyll-sass-converter (>= 2.0, < 4.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
kramdown (~> 2.3, >= 2.3.1)
|
||||
kramdown-parser-gfm (~> 1.0)
|
||||
liquid (~> 4.0)
|
||||
mercenary (>= 0.3.6, < 0.5)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 3.0, < 5.0)
|
||||
safe_yaml (~> 1.0)
|
||||
terminal-table (>= 1.8, < 4.0)
|
||||
webrick (~> 1.7)
|
||||
jekyll-feed (0.17.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-gist (1.5.0)
|
||||
octokit (~> 4.2)
|
||||
jekyll-sass-converter (3.0.0)
|
||||
sass-embedded (~> 1.54)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-tidy (0.2.2)
|
||||
htmlbeautifier
|
||||
htmlcompressor
|
||||
jekyll
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
kramdown (2.4.0)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.4)
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mdl (0.12.0)
|
||||
kramdown (~> 2.3)
|
||||
kramdown-parser-gfm (~> 1.1)
|
||||
mixlib-cli (~> 2.1, >= 2.1.1)
|
||||
mixlib-config (>= 2.2.1, < 4)
|
||||
mixlib-shellout
|
||||
mercenary (0.4.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
mixlib-cli (2.1.8)
|
||||
mixlib-config (3.0.27)
|
||||
tomlrb
|
||||
mixlib-shellout (3.2.7)
|
||||
chef-utils
|
||||
nokogiri (1.14.1-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
octokit (4.25.1)
|
||||
faraday (>= 1, < 3)
|
||||
sawyer (~> 0.9)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (5.0.1)
|
||||
racc (1.6.2)
|
||||
rake (13.0.6)
|
||||
rb-fsevent (0.11.2)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
reverse_markdown (2.1.1)
|
||||
nokogiri
|
||||
rexml (3.2.5)
|
||||
rouge (4.0.1)
|
||||
rss (0.2.9)
|
||||
rexml
|
||||
ruby2_keywords (0.0.5)
|
||||
safe_yaml (1.0.5)
|
||||
sass-embedded (1.58.0)
|
||||
google-protobuf (~> 3.21)
|
||||
rake (>= 10.0.0)
|
||||
sawyer (0.9.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (>= 0.17.3, < 3)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
tomlrb (2.0.3)
|
||||
unicode-display_width (2.4.2)
|
||||
webrick (1.8.1)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
http_parser.rb (~> 0.6.0)
|
||||
jekyll (~> 4.3.2)
|
||||
jekyll-feed (~> 0.12)
|
||||
jekyll-gist
|
||||
jekyll-import!
|
||||
jekyll-tidy
|
||||
mdl
|
||||
minima (~> 2.5)
|
||||
no-style-please!
|
||||
rss
|
||||
safe_yaml
|
||||
tzinfo (>= 1, < 3)
|
||||
tzinfo-data
|
||||
wdm (~> 0.1.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.26
|
23
README.md
Normal file
23
README.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# atomaka.com
|
||||
|
||||
Website managed by jekyll.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
* Python
|
||||
* whatever version lets you install `requirements.txt`
|
||||
* Ruby
|
||||
* Preferably the version in `.ruby-version`
|
||||
|
||||
## Setup
|
||||
|
||||
1. `pip install -r requirements.txt`
|
||||
2. `bundle install`
|
||||
|
||||
## Testing
|
||||
|
||||
* `bundle exec jekyll serve`
|
||||
|
||||
## Deployment
|
||||
|
||||
* `./build.sh`
|
60
_config.yml
Normal file
60
_config.yml
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Welcome to Jekyll!
|
||||
#
|
||||
# This config file is meant for settings that affect your whole blog, values
|
||||
# which you are expected to set up once and rarely edit after that. If you find
|
||||
# yourself editing this file very often, consider using Jekyll's data files
|
||||
# feature for the data you need to update frequently.
|
||||
#
|
||||
# For technical reasons, this file is *NOT* reloaded automatically when you use
|
||||
# 'bundle exec jekyll serve'. If you change this file, please restart the server process.
|
||||
#
|
||||
# If you need help with YAML syntax, here are some quick references for you:
|
||||
# https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml
|
||||
# https://learnxinyminutes.com/docs/yaml/
|
||||
#
|
||||
# Site settings
|
||||
# These are used to personalize your new site. If you look in the HTML files,
|
||||
# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on.
|
||||
# You can create any custom variable you would like, and they will be accessible
|
||||
# in the templates via {{ site.myvariable }}.
|
||||
|
||||
title: Andrew Tomaka
|
||||
email: atomaka@atomaka.com
|
||||
description: >- # this means to ignore newlines until "baseurl:"
|
||||
Personal website of Andrew Tomaka
|
||||
baseurl: "" # the subpath of your site, e.g. /blog
|
||||
url: "https://www.atomaka.com" # the base hostname & protocol for your site, e.g. http://example.com
|
||||
github_username: atomaka
|
||||
favicon: favicon.ico
|
||||
|
||||
# Build settings
|
||||
theme: no-style-please
|
||||
theme_config:
|
||||
appearance: "auto" # can be "light", "dark" or "auto"
|
||||
plugins:
|
||||
- jekyll-feed
|
||||
- jekyll-gist
|
||||
|
||||
# Exclude from processing.
|
||||
# The following items will not be processed, by default.
|
||||
# Any item listed under the `exclude:` key here will be automatically added to
|
||||
# the internal "default list".
|
||||
#
|
||||
# Excluded items can be processed by explicitly listing the directories or
|
||||
# their entries' file path in the `include:` list.
|
||||
#
|
||||
exclude:
|
||||
- .ruby-version
|
||||
- build.sh
|
||||
- old/
|
||||
- README.md
|
||||
# - .sass-cache/
|
||||
# - .jekyll-cache/
|
||||
# - gemfiles/
|
||||
# - Gemfile
|
||||
# - Gemfile.lock
|
||||
# - node_modules/
|
||||
# - vendor/bundle/
|
||||
# - vendor/cache/
|
||||
# - vendor/gems/
|
||||
# - vendor/ruby/
|
27
_data/menu.yml
Normal file
27
_data/menu.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
# For documentation on this file, see:
|
||||
# https://github.com/riggraz/no-style-please#customize-the-menu
|
||||
|
||||
entries:
|
||||
- title: info
|
||||
url: false
|
||||
entries:
|
||||
- title: coding
|
||||
url: https://github.com/atomaka
|
||||
- title: emailing
|
||||
url: mailto:atomaka@atomaka.com
|
||||
- title: speaking
|
||||
url: https://slides.com/atomaka
|
||||
- title: tooting
|
||||
url: https://pub.atomaka.com/@atomaka
|
||||
- title: working
|
||||
url: https://www.linkedin.com/in/atomaka
|
||||
|
||||
- title: all posts
|
||||
post_list:
|
||||
limit: 5
|
||||
show_more: true
|
||||
show_more_text: See archive...
|
||||
show_more_url: archive
|
||||
|
||||
- title: rss
|
||||
url: feed.xml
|
61
_posts/2012-10-25-force-print-document-on-11x17-scaled.md
Normal file
61
_posts/2012-10-25-force-print-document-on-11x17-scaled.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
layout: post
|
||||
title: Force "Print Document on" 11x17 Scaled
|
||||
tag:
|
||||
- technical
|
||||
---
|
||||
|
||||
The Print Spooler API in Windows does not seem to have an option to force
|
||||
scaling to another paper size. Formerly, we would install the printer save the
|
||||
registry key in `HKCU\Printers\DevModePerUser`. Then, we could check the "Print
|
||||
Document On" option, apply the settings, and save the registry key again.
|
||||
Performing a diff of between the two sets of roughly 4000 hex values should give
|
||||
a subset of values that relate to "Print Document On." Finally, on installation
|
||||
we could read in the registry key after everything else had been setup, cycle
|
||||
through it changing the appropriate values based on our diff and then save the
|
||||
registry key. This stopped working.
|
||||
|
||||
No new diffs could be collected that would update the scale to fit functionality
|
||||
that we needed. However, if the "Print Document On" option is manually set and
|
||||
the registry key is collected, that key can be used as the "diff" and the newly
|
||||
added printer would print scaled as desired. This has the unfortunate side
|
||||
effect of modifying all other settings on the printer including the name and
|
||||
color settings. As a work around, two different registry key "diffs" are used:
|
||||
one for color and one for black&white. Then, the first 30 hex characters can be
|
||||
chopped off in each key to make sure the printer name is not overwritten.
|
||||
|
||||
```
|
||||
#include <windows.h>;
|
||||
|
||||
int stf[][2] = {
|
||||
{8, 0},
|
||||
{10, 0},
|
||||
// and more keys
|
||||
};
|
||||
|
||||
int setPrintDocumentOn(char *printerName) {
|
||||
HKEY hkey;
|
||||
|
||||
// find the tree where the key we need to change resides
|
||||
RegOpenKey(HKEY_CURRENT_USER, "Printers\\DevModePerUser", &hkey);
|
||||
|
||||
DWORD requiredSize = 0;
|
||||
DWORD dataType;
|
||||
|
||||
// read in the key
|
||||
RegQueryValueEx(hkey, printerName, 0, &dataType, NULL, &requiredSize);
|
||||
char *DevModePerUserData = malloc(requiredSize);
|
||||
RegQueryValueEx(hkey, printerName, 0, &dataType, (BYTE *)DevModePerUserData, &requiredSize);
|
||||
|
||||
// update the key
|
||||
int i = 0;
|
||||
for(i = 0; i < sizeof(stf) / 8; i++) {
|
||||
DevModePerUserData[stf[i][0]] = stf[i][1];
|
||||
}
|
||||
|
||||
// and save our updates
|
||||
RegSetValueEx(hkey, printerName, 0, REG_BINARY, (BYTE *)DevModePerUserData, requiredSize);
|
||||
RegFlushKey(hkey);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
```
|
14
_posts/2013-02-02-serious-regular-expressions.md
Normal file
14
_posts/2013-02-02-serious-regular-expressions.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
layout: post
|
||||
title: Serious Regular Expressions
|
||||
tag:
|
||||
- humor
|
||||
---
|
||||
|
||||
```
|
||||
$n = '[0-9]';
|
||||
$reg = '/^[AZ]'.$n.$n.$n.$n.$n.$n.$n.$n.'$/';
|
||||
if(preg_match($reg, $id)) {
|
||||
// ...
|
||||
}
|
||||
```
|
58
_posts/2013-09-25-password-checking-in-c.md
Normal file
58
_posts/2013-09-25-password-checking-in-c.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
layout: post
|
||||
title: Password Checking in C
|
||||
tag:
|
||||
- humor
|
||||
---
|
||||
|
||||
```
|
||||
if (strlen(encrytedenteredpassword) != 13) {
|
||||
passwordcorrect=0;
|
||||
} else {
|
||||
for (i=0; i<13;i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
if (encrytedenteredpassword[i] != 'f') passwordcorrect=0;
|
||||
break;
|
||||
case 1:
|
||||
if (encrytedenteredpassword[i] != 'J') passwordcorrect=0;
|
||||
break;
|
||||
case 2:
|
||||
if (encrytedenteredpassword[i] != 'c') passwordcorrect=0;
|
||||
break;
|
||||
case 3:
|
||||
if (encrytedenteredpassword[i] != 'l') passwordcorrect=0;
|
||||
break;
|
||||
case 4:
|
||||
if (encrytedenteredpassword[i] != 'Q') passwordcorrect=0;
|
||||
break;
|
||||
case 5:
|
||||
if (encrytedenteredpassword[i] != 'v') passwordcorrect=0;
|
||||
break;
|
||||
case 6:
|
||||
if (encrytedenteredpassword[i] != 'P') passwordcorrect=0;
|
||||
break;
|
||||
case 7:
|
||||
if (encrytedenteredpassword[i] != 'i') passwordcorrect=0;
|
||||
break;
|
||||
case 8:
|
||||
if (encrytedenteredpassword[i] != 'l') passwordcorrect=0;
|
||||
break;
|
||||
case 9:
|
||||
if (encrytedenteredpassword[i] != 'N') passwordcorrect=0;
|
||||
break;
|
||||
case 10:
|
||||
if (encrytedenteredpassword[i] != 'A') passwordcorrect=0;
|
||||
break;
|
||||
case 11:
|
||||
if (encrytedenteredpassword[i] != 'z') passwordcorrect=0;
|
||||
break;
|
||||
case 12:
|
||||
if (encrytedenteredpassword[i] != '.') passwordcorrect=0;
|
||||
break;
|
||||
default: passwordcorrect=0; break;
|
||||
}
|
||||
if (!passwordcorrect) break;
|
||||
}
|
||||
}
|
||||
```
|
300
_posts/2014-01-19-intro-to-puppet.md
Normal file
300
_posts/2014-01-19-intro-to-puppet.md
Normal file
|
@ -0,0 +1,300 @@
|
|||
---
|
||||
layout: post
|
||||
title: 'Intro to Puppet: The Bare Minimum'
|
||||
tag:
|
||||
- puppet
|
||||
- technical
|
||||
---
|
||||
|
||||
Last month, some of my coworkers were looking for a brief introduction to
|
||||
[Puppet](http://www.puppetlabs.com/). Puppet is a type of configuration manager
|
||||
for your servers. It allows you to create definitions of your server that can
|
||||
then be automatically maintained. Puppet is mostly self documenting so it makes
|
||||
it easy to know what your servers are doing while giving you a great way to
|
||||
automate setting up large numbers of servers.
|
||||
|
||||
This is that brief talk. All code is available on
|
||||
[Github in my puppet-walkthru repository](https://github.com/atomaka/puppet-walkthru).
|
||||
You will need [Git](http://www.git-scm.com/),
|
||||
[Virtual Box](https://www.virtualbox.org/) and
|
||||
[Vagrant](http://www.vagrantup.com/) installed. To begin, clone the repository
|
||||
and launch the Vagrantfile:
|
||||
|
||||
```
|
||||
git clone https://github.com/atomaka/puppet-walkthru.git
|
||||
cd puppet-walkthru
|
||||
vagrant up
|
||||
```
|
||||
|
||||
This will setup a virtual machine on your computer with Puppet installed. All
|
||||
code can be found on the virtual machine in the /vagrant directory.
|
||||
|
||||
```
|
||||
vagrant ssh
|
||||
sudo su cd /vagrant
|
||||
```
|
||||
|
||||
You are now ready to work through the first example.
|
||||
|
||||
## 1. Managing Users
|
||||
|
||||
Puppet maintains state on your computer using what are referred to as
|
||||
[resources](http://docs.puppetlabs.com/references/latest/type.html). The
|
||||
built-in resources provided by Puppet provide a good start. In
|
||||
[example one](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/1-user-type.pp),
|
||||
you can see how to use a Puppet resource to add and remove a user.
|
||||
|
||||
```
|
||||
user { 'tm':
|
||||
ensure => present,
|
||||
}
|
||||
user { 'fowlks':
|
||||
ensure => absent,
|
||||
}
|
||||
```
|
||||
|
||||
You can run this code on your virtual machine with
|
||||
`puppet apply manifests/1-user-type.pp`. Afterward, you should notice that the
|
||||
user "tm" exists on your system.
|
||||
|
||||
The [user resource](http://docs.puppetlabs.com/references/latest/type.html#user)
|
||||
type manages local users on your system. This works on a wide variety of
|
||||
systems, although some do not support some of the more specific features. In
|
||||
this example, we make sure the user "tm" is present on the system and make sure
|
||||
the user "fowlks" is not present.
|
||||
|
||||
ensure is a keyword for all Puppet resources. present and absent are the most
|
||||
common values although some resource types have others. ensure will make sure
|
||||
that definition exists on your server and absent will obviously do the opposite.
|
||||
|
||||
## 2. Managing Files
|
||||
|
||||
Managing files is one of the most common tasks for server administration and
|
||||
Puppet offers many ways to handle this. We’ll explore these in the
|
||||
[next example](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/2-file-type.pp).
|
||||
|
||||
```
|
||||
file { '/tmp/test1.txt':
|
||||
ensure => present,
|
||||
content => 'Hello',
|
||||
}
|
||||
file { '/tmp/test2.txt':
|
||||
ensure => present,
|
||||
source => '/vagrant/files/test2.txt',
|
||||
}
|
||||
$something = "Hello"
|
||||
file { '/tmp/test3.txt':
|
||||
ensure => present,
|
||||
content => template('/vagrant/templates/test3.txt.erb'),
|
||||
}
|
||||
```
|
||||
|
||||
Run this on your virtual machine using `puppet apply manifests/2-file-type.pp`
|
||||
and you should be alerted that three files were created. You can verify this by
|
||||
viewing the contents of the tmp directory with `ls /tmp`.
|
||||
|
||||
The first
|
||||
[file resource](http://docs.puppetlabs.com/references/latest/type.html#file)
|
||||
simply creates a file at the specified location that says "Hello."
|
||||
Unfortunately, this isn’t very useful since we do not want to have to type our
|
||||
entire cnfiguration file in our Puppet definition. The second resource is
|
||||
slightly more useful. This allows us to copy a file from our Puppet repository
|
||||
to a specified location.
|
||||
|
||||
Finally, we can also create templates. The last example uses a file from our
|
||||
repository and copies it to the specified location. However, we can also include
|
||||
variables that can be used in our file. In this case, we set a variable to
|
||||
something and it is then displayed in the file: `You said: Hello`. The contents
|
||||
of `$something` are used in the file.
|
||||
|
||||
## 3. Installing Packages
|
||||
|
||||
The last common task we’ll look at is installing packages. Puppet provides a way
|
||||
to define which
|
||||
[packages](http://docs.puppetlabs.com/references/latest/type.html#package) can
|
||||
be installed. By default, this uses your distributions built-in package manager
|
||||
although there are ways to specify various providers. Our example
|
||||
[shows the most basic usage](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/3-package-type.pp).
|
||||
|
||||
```
|
||||
package { 'vim':
|
||||
ensure => present,
|
||||
}
|
||||
package { 'alpine-pico':
|
||||
ensure => absent,
|
||||
}
|
||||
```
|
||||
|
||||
Try to open vim and you will notice it cannot run. Once you run this code with
|
||||
`puppet apply manifests/3-package-type.pp`, the vim package will then be
|
||||
present.
|
||||
|
||||
## 4. Ordering (or lack thereof)
|
||||
|
||||
The trickest thing for beginners to Puppet is dealing with its
|
||||
[non-deterministic behavior](http://puppetlabs.com/blog/inside-puppet-about-determinism).
|
||||
This is easier to
|
||||
[show than explain](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/4-order-example.pp).
|
||||
|
||||
```
|
||||
notify { 'First': }
|
||||
notify { 'Second': }
|
||||
notify { 'Third': }
|
||||
notify { 'Fourth': }
|
||||
notify { 'Fifth': }
|
||||
notify { 'Sixth': }
|
||||
notify { 'Seventh': }
|
||||
```
|
||||
|
||||
When run, you would expect this to spit out First, Second, …, Seventh in order.
|
||||
Invoke this code with `puppet apply manifests/4-order-example.pp` and be
|
||||
surprised at the results. The order of the code is much different than what is
|
||||
in the file. Furthermore, if you were to add `notify { 'Eighth': }` the ordering
|
||||
might change completely.
|
||||
|
||||
## 5. But I Need Order
|
||||
|
||||
But there are dependencies when setting up systems. Puppet allows for this, you
|
||||
just are required to
|
||||
[explicitly define them](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/5-ordered-example.pp).
|
||||
The biggest advantage here is that if one line of dependencies fails, your
|
||||
entire configuration does not. It takes some getting used to and can be
|
||||
frustrating, but it is worth it.
|
||||
|
||||
```
|
||||
notify { 'First': }
|
||||
notify { 'Second':
|
||||
require => Notify['First'],
|
||||
}
|
||||
notify { 'Third':
|
||||
require => Notify['Second'],
|
||||
}
|
||||
notify { 'Fourth':
|
||||
require => Notify['Third'],
|
||||
}
|
||||
notify { 'Fifth':
|
||||
require => Notify['Fourth'],
|
||||
}
|
||||
notify { 'Sixth':
|
||||
require => Notify['Fifth'],
|
||||
}
|
||||
notify { 'Seventh':
|
||||
require => Notify['Sixth'],
|
||||
}
|
||||
```
|
||||
|
||||
By using the `require` parameter, we have have forced ordering. If you run this
|
||||
code with `puppet apply manifests/5-ordered-example.pp`, you will see the order
|
||||
you expected in example number four.
|
||||
|
||||
## 6. Know Your Environment
|
||||
|
||||
Puppet also provides a way for you to know about the system that the Puppet code
|
||||
is running on with a system called Facter.
|
||||
|
||||
```
|
||||
notify { "${::osfamily}": }
|
||||
notify { "${::ipaddress}": }
|
||||
notify { "${::uptime}": }
|
||||
```
|
||||
|
||||
When run with `puppet apply manifests/6-facts-example.pp`,
|
||||
[this code](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/6-facts-example.pp)
|
||||
will display the information about the virtual machine you are running on. We
|
||||
will look at why this is useful later.
|
||||
|
||||
## 7. Doing Something Useful
|
||||
|
||||
Now that we have seen some quick forced examples of how to use Puppet, we now
|
||||
have enough knowledge to do something that is actually useful. Using Puppet, we
|
||||
can
|
||||
[configure an entire service](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/7-full-example.pp).
|
||||
If you are not familiar, [NTP](http://www.ntp.org/) is a networking protocol for
|
||||
time management. It is useful for mainitaining the same system time across all
|
||||
of your servers. And we can use Puppet to install it!
|
||||
|
||||
```
|
||||
package { 'ntp':
|
||||
ensure => present,
|
||||
}
|
||||
file { '/etc/ntp.conf':
|
||||
ensure => present,
|
||||
require => Package['ntp'],
|
||||
source => '/vagrant/files/ntp.conf.debian',
|
||||
}
|
||||
service { 'ntp':
|
||||
ensure => running,
|
||||
enable => true,
|
||||
subscribe => File['/etc/ntp.conf'],
|
||||
}
|
||||
```
|
||||
|
||||
When running this code with `puppet apply manifest/7-full-example.pp`, you
|
||||
should notice three things happen. First, the ntp package will be installed.
|
||||
Since we are on Ubuntu, this is done using apt-get. Secondly, a configuration
|
||||
file was copied from our Puppet repository to the location specified. Finally,
|
||||
the ntp service was started.
|
||||
|
||||
Install, configure, start is one of the most common patterns in Linux/UNIX
|
||||
systems administration and we can easily automate it with Puppet.
|
||||
|
||||
Something to note is our use of subscribe when using the
|
||||
[service resource](http://docs.puppetlabs.com/references/latest/type.html#service)
|
||||
type. This makes sure that the ntp service is restarted only if the
|
||||
configuration file has changed.
|
||||
|
||||
## 7. Managing Multiple Operating Systems
|
||||
|
||||
Before this section, be sure to reset what we did in the previous example by
|
||||
running bash support/cleanup7.sh. We just need to uninstall ntp and our config
|
||||
file so we can do it all again.
|
||||
|
||||
Unfortunately, our environments are never uniform and we are stuck dealing with
|
||||
different versions of operating systems. Luckily, we have tools that we can use
|
||||
to deal with it. We touched on this in section six, but now we will actually use
|
||||
them
|
||||
[to install ntp again](https://github.com/atomaka/puppet-walkthru/blob/master/manifests/8-independent-example.pp).
|
||||
This time, our code will work on both Debian and RedHat family Linux
|
||||
distributions.
|
||||
|
||||
```
|
||||
case $::osfamily {
|
||||
'RedHat': {
|
||||
$service = 'ntpd'
|
||||
$conf = 'ntp.conf.redhat'
|
||||
}
|
||||
'Debian': {
|
||||
$service = 'ntp'
|
||||
$conf = 'ntp.conf.debian'
|
||||
}
|
||||
}
|
||||
notify { 'OS Information':
|
||||
message => "${::osfamily}: Setting service to ${service} and conf to ${conf}",
|
||||
before => Package['ntp'],
|
||||
}
|
||||
package { 'ntp':
|
||||
ensure => present,
|
||||
}
|
||||
file { '/etc/ntp.conf':
|
||||
ensure => present,
|
||||
require => Package['ntp'],
|
||||
source => "/vagrant/files/${conf}",
|
||||
}
|
||||
service { $service:
|
||||
ensure => running,
|
||||
enable => true,
|
||||
subscribe => File['/etc/ntp.conf'],
|
||||
}
|
||||
```
|
||||
|
||||
When on our Ubuntu virtual machine, running this code with
|
||||
`puppet apply manifest/8-independent-example.pp` will setup NTP just as we did
|
||||
in example seven. However, this code can also run on RedHat / CentOS machines
|
||||
without any adjustments.
|
||||
|
||||
This is handled using the facts we discussed in section six. We check to see
|
||||
what distribution we are using with `$::osfamily` and make choices based on
|
||||
that. Since the service and the config file are different on RedHat, we assign
|
||||
variables to these and adjust them as needed.
|
||||
|
||||
You now have enough knowledge to get started with Puppet!
|
78
_posts/2014-02-11-get-away-from-me-you-creep.md
Normal file
78
_posts/2014-02-11-get-away-from-me-you-creep.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
layout: post
|
||||
title: Get Away From Me You Creep
|
||||
tag:
|
||||
- humor
|
||||
---
|
||||
|
||||
Almost ten years ago, I took a telecommunication history with course with one of
|
||||
the most eccentric people I had ever met. Unfortunately, it was during a time
|
||||
where I was attending very few courses and I missed most of my opportunities to
|
||||
listen to him speak. Still, I managed to make it to enough classes to have
|
||||
distinct memories of him. Dr. Barry Litman passed away a few years ago and these
|
||||
are his stories.
|
||||
|
||||
I attended the first day of class just as I did the first day of every class I
|
||||
took. I sat down and was probably scribbling in the notepad I purchased that I
|
||||
knew would never be used or barely staying awake. He began giving a brief
|
||||
overview the history of telecommunication in extreme story telling mode with an
|
||||
unprecedented excitement. I don’t remember anything from that day except for the
|
||||
way he ended class: “And just like that, the entire industry evolved into a star
|
||||
just waiting to supernova.”
|
||||
|
||||
I explained this to a couple of students I was working with and they immediately
|
||||
knew who I was talking about. They warned me about David Sarnoff but would not
|
||||
give me any other information. I was forced to attend class for several sessions
|
||||
before the story was revealed.
|
||||
|
||||
David Sarnoff was a corporate kingpin during the pioneering of radio and
|
||||
television serving as an executive for the Radio Corporation of America. Dr.
|
||||
Litman was visibly agitated as he explained this and other RCA history to the
|
||||
class. And then he began talking about Edwin Armstrong.
|
||||
|
||||
Edwin Armstrong was an employee of RCA and eventually credited inventor of FM
|
||||
radio. He developed this while working for RCA, but Sarnoff saw it as a threat
|
||||
to the AM technology that the company was already producing. Because of this and
|
||||
their focus on television, Sarnoff and RCA chose not to purchase the patents for
|
||||
the technology. But after hearing the quality and seeing its success, RCA first
|
||||
tried to have the FCC ban usage of the wave and eventually claimed the invention
|
||||
of the technology and won the patent. Armstrong spent nearly all his time and
|
||||
money fighting for the rights to his own technology.
|
||||
|
||||
As he was explaining these details, Litman went from agitated to infuriated. His
|
||||
face reddened, he began sweating and spoke louder and louder. The entire class
|
||||
was looking around at each other and no one knew how to respond.
|
||||
|
||||
He eventually came to the close of the story. Armstrong ended up committing
|
||||
suicide. And even though he was eventually given the credit he deserved, it came
|
||||
too late. The professor was visibly exhausted and saddened but managed to
|
||||
literally spit out one final angry yell: “And it was all because of that son of
|
||||
a bitch, David Sarnoff.” He then walked out of the room.
|
||||
|
||||
I mostly stopped going to class after that. I don’t know why. He was an
|
||||
incredible teacher and his enthusiasm kept me interested during most lectures.
|
||||
In the few classes I did attend he frequently referenced Vanna White as someone
|
||||
who used to be one of his favorite people, but never expanded the story. The
|
||||
last day before finals I went to class in hopes of getting some hints for the
|
||||
final. Instead, he shared his story.
|
||||
|
||||
He explained to us that once a year, he and one of his close friends/colleagues
|
||||
would attend a conference related to telecommunication. A few years prior, Vanna
|
||||
White was attending something loosely related and he managed to convince his
|
||||
friend to attend. Vanna White’s booth was highly trafficked and Litman was
|
||||
forced to wait in line for three hours to see her. During this time, he was
|
||||
extremely nervous which led to him soaking through his shirt with sweat. When it
|
||||
was finally his turn to meet her, he walked up and blurted out about how big of
|
||||
a fan he was and how long he had followed her.
|
||||
|
||||
And then he paused. I remember it feeling like ten minutes, but it was probably
|
||||
closer to one. Finally, he asked the class “And do you know what that bitch said
|
||||
to me?” He walked over to the side of the classroom and held the button to
|
||||
retract the overhead. Slowly, the words she spoke were revealed having been
|
||||
written in chalk on the board before the class had begun:
|
||||
|
||||
"Get away from me, you creep."
|
||||
|
||||
I wish I had written about these ten years ago. Even then, I wouldn’t have done
|
||||
the man justice. Truly great speakers are rare and it’s unfortunate he passed so
|
||||
soon.
|
143
_posts/2015-11-21-dockerizing-lubot.md
Normal file
143
_posts/2015-11-21-dockerizing-lubot.md
Normal file
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
layout: post
|
||||
title: Dockerizing Lubot
|
||||
tag:
|
||||
- docker
|
||||
- technical
|
||||
---
|
||||
|
||||
Lubot is the [Lansing Codes](http://lansing.codes/) chat bot responsible for
|
||||
bringing updates about all local events to chatters everywhere…or just in
|
||||
Lansing. Previously, it resided on a Heroku Free instance and was required to
|
||||
sleep for six hours out of every twenty-four hours. After some issues with him
|
||||
waking up, we began looking for alternatives. Since I already had a server
|
||||
hosting several Docker containers, it seemed like the best choice.
|
||||
|
||||
Docker is basically a way to create a container that can be easily distributed
|
||||
across many operating systems. Using it, we can take the code, runtime, and
|
||||
libraries that Lubot requires and put them in a package. You can read more about
|
||||
it in the
|
||||
[Official “What is Docker” article](https://www.docker.com/what-docker).
|
||||
|
||||
To begin, we need to determine the necessary dependencies for our application.
|
||||
Lubot is built using NodeJS and uses npm to manage dependencies. Specifically,
|
||||
we are using Node v5.0.0 and npm v3.3.9. There’s an official Node Dockerfile to
|
||||
use to begin with so it is pretty easy to start.
|
||||
|
||||
```
|
||||
FROM node
|
||||
|
||||
ENV NODE_VERSION 5.0.0
|
||||
ENV NPM_VERSION 3.3.9
|
||||
```
|
||||
|
||||
After that, we want to take care of the dependencies for our application.
|
||||
Because of the way Docker works, we want to cache this step so when our
|
||||
packages.json file does not change, we do not have to rebuild our dependencies.
|
||||
|
||||
```
|
||||
ADD package.json /tmp/
|
||||
RUN cd /tmp && npm install
|
||||
RUN mkdir -p /opt/hubot && cp -a /tmp/node_modules /opt/hubot
|
||||
```
|
||||
|
||||
Then, we need to add the application code to the container.
|
||||
|
||||
```
|
||||
ADD . /opt/hubot
|
||||
WORKDIR /opt/hubot
|
||||
```
|
||||
|
||||
Finally, we can start the service.
|
||||
|
||||
```
|
||||
CMD ["/opt/hubot/bin/hubot", "--adapter", "slack"]
|
||||
```
|
||||
|
||||
Combine these steps and we end up with a
|
||||
[Dockerfile](https://github.com/lansingcodes/lubot/blob/master/Dockerfile). This
|
||||
gets added to the repisitory so that we can build the application. Building an
|
||||
image is easy.
|
||||
|
||||
```
|
||||
docker build -t lansingcodes/lubot .
|
||||
```
|
||||
|
||||
This will download and build the necessary filesystems, caching where necessary
|
||||
and giving us a runable container image. Starting the container is also simple.
|
||||
|
||||
```
|
||||
docker run lansingcodes/lubot
|
||||
```
|
||||
|
||||
Lubot expects some environment variables to be there. But since we are in a
|
||||
container, no environment variables exist on the system and we need to pass them
|
||||
in. Our new run command accounts for this.
|
||||
|
||||
```
|
||||
docker run -d --restart=always --name lubot \
|
||||
-e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN \
|
||||
-e TWITTER_LANSINGCODES_CONSUMER_KEY=$TWITTER_LANSINGCODES_CONSUMER_KEY \
|
||||
-e TWITTER_LANSINGCODES_CONSUMER_SECRET=$TWITTER_LANSINGCODES_CONSUMER_SECRET \
|
||||
-e TWITTER_LANSINGCODES_ACCESS_TOKEN=$TWITTER_LANSINGCODES_ACCESS_TOKEN \
|
||||
-e TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET=$TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET \
|
||||
-e GOOGLE_API_KEY=$GOOGLE_API_KEY \
|
||||
-e LUBOT_MEETUP_API_KEY=$LUBOT_MEETUP_API_KEY \
|
||||
-e TZ=$TZ \
|
||||
-e REDIS_URL=$REDIS_URL \
|
||||
lansingcodes/lubot
|
||||
```
|
||||
|
||||
Lubot is now running in a container. However, Heroku also provided easy
|
||||
continuous deployment when combined with [Circle CI](https://circleci.com/).
|
||||
Being able to have changes deployed when the master branch changes is handy.
|
||||
Circle CI allows us to specify post-build commands to run. Typically, we’d want
|
||||
to build the container on our CI server and then push to a Docker registry, butI
|
||||
didn’t have one of those available. We can still use Circle CI to execute
|
||||
commands on a remote server with SSH. This makes our deploy process simple.
|
||||
|
||||
- clone the repository on our remote server
|
||||
- build the docker image from that repositry
|
||||
- run the docker image that was build
|
||||
|
||||
Our CI build file will trigger these actions via a script.
|
||||
|
||||
```
|
||||
scp deploy/deploy.sh lubot@app.atomaka.com:/home/lubot
|
||||
ssh lubot@app.atomaka.com "bash /home/lubot/deploy.sh"
|
||||
```
|
||||
|
||||
And then, deploy.sh will take care of the parts we already discussed.
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
cd $HOME
|
||||
source lubotrc
|
||||
|
||||
git clone https://github.com/lansingcodes/lubot.git
|
||||
|
||||
cd $HOME/lubot
|
||||
sudo docker build -t lansingcodes/lubot .
|
||||
cd $HOME
|
||||
rm -rf $HOME/lubot
|
||||
|
||||
sudo docker rm -f lubot
|
||||
sudo docker run -d --restart=always --name lubot \
|
||||
-e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN \
|
||||
-e TWITTER_LANSINGCODES_CONSUMER_KEY=$TWITTER_LANSINGCODES_CONSUMER_KEY \
|
||||
-e TWITTER_LANSINGCODES_CONSUMER_SECRET=$TWITTER_LANSINGCODES_CONSUMER_SECRET \
|
||||
-e TWITTER_LANSINGCODES_ACCESS_TOKEN=$TWITTER_LANSINGCODES_ACCESS_TOKEN \
|
||||
-e TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET=$TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET \
|
||||
-e GOOGLE_API_KEY=$GOOGLE_API_KEY \
|
||||
-e LUBOT_MEETUP_API_KEY=$LUBOT_MEETUP_API_KEY \
|
||||
-e TZ=$TZ \
|
||||
-e REDIS_URL=$REDIS_URL \
|
||||
lansingcodes/lubot
|
||||
```
|
||||
|
||||
Deploying Lubot is now just as easy as it was with Heroku and he never has to
|
||||
sleep again.
|
||||
|
||||
- [Other details surrounding the deployment](https://github.com/lansingcodes/lubot/tree/master/deploy)
|
||||
- [Lubot repository](https://github.com/lansingcodes/slackbot)
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
layout: post
|
||||
title: Testing Your Docker Images with GitLab CI
|
||||
tag:
|
||||
- continuous-integration
|
||||
- gitlab
|
||||
- docker
|
||||
- devops
|
||||
---
|
||||
|
||||
I have been using [GitLab](https://about.gitlab.com) for over four years. My
|
||||
[first commit](https://github.com/gitlabhq/gitlabhq/commit/0760ba3efb7566b9f56bb066f4b15ba8ea34e1e7)
|
||||
to the project came nearly three years ago. And although I was pretty
|
||||
disappointed when they began
|
||||
[releasing an enterprise edition](https://about.gitlab.com/2013/07/22/announcing-gitlab-enterprise-edition/),
|
||||
the community edition of the project remains impressive. More recently, GitLab
|
||||
has included a
|
||||
[continuous integration software](https://about.gitlab.com/gitlab-ci/) along
|
||||
with their code collaboration solution.
|
||||
|
||||
Recently, I have been using this to produce [Docker](https://www.docker.com/)
|
||||
images for my production environment. Although I had been using Docker for all
|
||||
of my build stages, I was never using the image I was producing for validation.
|
||||
Since I want to be as sure as I can that my production images are functioning, I
|
||||
decided to update my build to use the project Dockerfile to run tests.
|
||||
|
||||
I looked around and found a
|
||||
[few](http://sirile.github.io/2016/09/29/minimal-go-containers-with-docker-and-gitlab-ci.html)
|
||||
[articles](https://www.andrewmunsell.com/blog/ci-cd-with-docker-containers/) on
|
||||
using Docker with Gitlab CI. Unfortunately, they all outlined methods that
|
||||
[did not test the image](https://www.stefanwienert.de/blog/2015/11/07/gitlab-ci-with-docker-starting-guide/)
|
||||
directly or did so in a
|
||||
[complicated way](http://blog.lwolf.org/post/How-to-build-and-test-docker-images-in-gitlab-ci/).
|
||||
I thought I could do better.
|
||||
|
||||
We always want to use Docker for our builds, but running Docker inside of Docker
|
||||
is not recommended. To resolve this, we can mount the host system’s Docker
|
||||
socket inside the container when creating our test runner for building images.
|
||||
|
||||
```
|
||||
[[runners]]
|
||||
name = "docker-runner"
|
||||
url = "https://gitlab.example.com/ci"
|
||||
token = "YOUR_TOKEN"
|
||||
executor = "docker"
|
||||
[runners.docker]
|
||||
tls_verify = false
|
||||
image = "docker:latest"
|
||||
privileged = false
|
||||
disable_cache = false
|
||||
volumes = ["/var/run/docker.sock:/var/run/docker.sock"]
|
||||
```
|
||||
|
||||
Now that we are using the host Docker, we can leverage its image storage and
|
||||
caching for our build. We can also use that image in our other build tasks.
|
||||
|
||||
```
|
||||
stages:
|
||||
- prepare
|
||||
- test
|
||||
- deploy
|
||||
|
||||
build-test-container:
|
||||
stage: prepare
|
||||
script:
|
||||
- docker build -t your-image .
|
||||
tags:
|
||||
- your-docker-tag
|
||||
|
||||
spec:
|
||||
stage: test
|
||||
script:
|
||||
- bundle exec rake db:create db:migrate
|
||||
- bundle exec rspec
|
||||
image: your-image
|
||||
services:
|
||||
- postgres:latest
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
The container built from our project Dockerfile is now being directly tested by
|
||||
our continuous integration. As you can see, we can also use any container links
|
||||
without writing extra code.
|
||||
|
||||
Clean and simple!
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
layout: post
|
||||
title: Singular and Plural Rails Routes for the Same Resource
|
||||
tag:
|
||||
- api
|
||||
- ruby-on-rails
|
||||
- restful
|
||||
---
|
||||
|
||||
Sometimes when building your API with Rails,
|
||||
[following best practices](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api)
|
||||
may seem difficult. I recently came across one of these cases and was tempted to
|
||||
take a shortcut. However, I held strong and eventually came to a quality RESTful
|
||||
solution — but not without issue.
|
||||
|
||||
I wanted to allow users of my API to quickly access their own user profile. Our
|
||||
application had already implemented a route to allow this via
|
||||
`GET /profiles/:id` and simply implemented with
|
||||
`resources :profiles, only: [:show]` in our routes configuration.
|
||||
|
||||
Unfortunately, our API users wanted to be able to access their profile without
|
||||
providing their ID. My first pass at resolving this was passed in a "fake
|
||||
resource" to accomplish this.
|
||||
|
||||
{% gist 1a056f8dca8931d8872c6cfefebb2d1a %}
|
||||
|
||||
But I had broken one of the RESTful best practices. /profiles/me is not an
|
||||
actual resource but we are pretending it is. So I looked to the
|
||||
[Rails routes documentation](http://guides.rubyonrails.org/routing.html) for
|
||||
guidance and came across
|
||||
[singular resources](http://guides.rubyonrails.org/routing.html#singular-resources).
|
||||
|
||||
> Sometimes, you have a resource that clients always look up without referencing
|
||||
> an ID. For example, you would like /profile to always show the profile of the
|
||||
> currently logged in user.
|
||||
|
||||
I should not have been surprised that my exact use case was cited!
|
||||
|
||||
Now we are back on track! I get to go back to my simple route declaration with
|
||||
`resource :profile, only: :show` and without changing my controller code at all.
|
||||
|
||||
But now I needed users to be able to access each other’s profiles. Again, the
|
||||
Rails documentation had me covered.
|
||||
|
||||
> Because you might want to use the same controller for a singular route
|
||||
> (/account) and a plural route (/accounts/45), singular resources map to plural
|
||||
> controllers. So that, for example, resource :photo and resources :photos
|
||||
> creates both singular and plural routes that map to the same controller
|
||||
> (PhotosController).
|
||||
|
||||
And our implementation stays clean.
|
||||
|
||||
{% gist e8d6641349e4d0ea7e68d22dd3755e9d %}
|
||||
|
||||
This was awesome until I needed to use path helpers. With this implementation,
|
||||
`profile_path(:id)` works as expected but `profile_path` does not. If the order
|
||||
is reversed in the routes configuration, `profile_path` will work and
|
||||
`profile_path(:id)` will not. This is the result of a bug in the Rails core that
|
||||
touches some pretty intricate code that is linked to other issues.
|
||||
[One has even been open for five years](https://github.com/rails/rails/issues/1769)!
|
||||
|
||||
And we can work around that one as well by
|
||||
[overriding the named helpers](http://guides.rubyonrails.org/routing.html#overriding-the-named-helpers).
|
||||
Passing as: to our resource definition creates the helpers with a new name.
|
||||
|
||||
Our final code is ready!
|
||||
|
||||
{% gist e9dcc4cd4bad89554fb01be6627c7b63 %}
|
||||
|
||||
In our application, we can reference a generic profile with `profile_path(:id)`
|
||||
while still having `current_profile_path` to direct the user to their
|
||||
own profile.
|
150
_posts/2017-03-01-a-more-flexible-dockerfile-for-rails.md
Normal file
150
_posts/2017-03-01-a-more-flexible-dockerfile-for-rails.md
Normal file
|
@ -0,0 +1,150 @@
|
|||
---
|
||||
layout: post
|
||||
title: A More Flexible Dockerfile for Rails
|
||||
tag:
|
||||
- rails
|
||||
- docker
|
||||
- devops
|
||||
---
|
||||
|
||||
One of my primary motivations for working with [Docker](https://www.docker.com/)
|
||||
was creating a single artifact that I could toss into any environment. It has
|
||||
been fantastic at this. I can throw together a simple Dockerfile that will build
|
||||
my [Rails](http://rubyonrails.org/) application as an image for production in
|
||||
about five minutes.
|
||||
|
||||
```
|
||||
FROM ruby:2.3-alpine
|
||||
|
||||
ADD Gemfile* /app/
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps build-base \
|
||||
&& apk add --no-cache postgresql-dev tzdata \
|
||||
&& cd /app; bundle install --without test production \
|
||||
&& apk del .build-deps
|
||||
|
||||
ADD . /app
|
||||
RUN chown -R nobody:nogroup /app
|
||||
USER nobody
|
||||
|
||||
ENV RAILS_ENV production
|
||||
|
||||
WORKDIR /app
|
||||
CMD ["bundle", "exec", "rails", "s", "-b", "0.0.0.0", "-p", "8080"]
|
||||
```
|
||||
|
||||
Except now that when I need to run the application’s test suite, I do not have
|
||||
the dependencies I need. That Dockerfile might look something like this.
|
||||
|
||||
```
|
||||
FROM ruby:2.3-alpine
|
||||
|
||||
RUN apk add --no-cache build-base postgresql-dev tzdata
|
||||
|
||||
ADD Gemfile* /app/
|
||||
|
||||
RUN cd /app; bundle install
|
||||
|
||||
ADD . /app
|
||||
RUN chown -R nobody:nogroup /app
|
||||
USER nobody
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
CMD ["bundle", "exec", "rails", "s", "-b", "0.0.0.0", "-p", "8080"]
|
||||
```
|
||||
|
||||
Many people decide to include both of these Dockerfiles in their repository as
|
||||
Dockerfile and Dockerfile.dev. This works perfectly fine. But now we have a
|
||||
production Dockerfile that never gets used during development. Of course, it is
|
||||
going through at least one staging environment (hopefully) but it would be nice
|
||||
if we had a little more testing against it.
|
||||
|
||||
Much like Docker provides us the ability to have a single artifact to move from
|
||||
system to system, I wanted to have a single Dockerfile shared between all
|
||||
environments. Luckily, Docker provides us with
|
||||
[build arguments](https://docs.docker.com/engine/reference/builder/#/arg). A
|
||||
build argument allows us to specify a variable when building the image and then
|
||||
use that variable inside our Dockerfile.
|
||||
|
||||
In our current Rails Dockerfile, we have two primary differences between our
|
||||
environments:
|
||||
|
||||
- The gem groups that are installed
|
||||
- The environment that the application runs as
|
||||
|
||||
Bundler’s
|
||||
[BUNDLE_WITHOUTBUNDLE_WITHOUT](http://bundler.io/man/bundle-config.1.html#LIST-OF-AVAILABLE-KEYS)
|
||||
allows us to specify the gem groups to skip via an environment variable making
|
||||
both of these resolvable through environment configuration. Using this, our
|
||||
shared Dockerfile could look like this:
|
||||
|
||||
```
|
||||
FROM ruby:2.3-alpine
|
||||
|
||||
ARG BUNDLE_WITHOUT=test:development
|
||||
ENV BUNDLE_WITHOUT ${BUNDLE_WITHOUT}
|
||||
|
||||
ADD Gemfile* /app/
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps build-base \
|
||||
&& apk add --no-cache postgresql-dev tzdata \
|
||||
&& cd /app; bundle install \
|
||||
&& apk del .build-deps
|
||||
|
||||
ADD . /app
|
||||
RUN chown -R nobody:nogroup /app
|
||||
USER nobody
|
||||
|
||||
ARG RAILS_ENV=production
|
||||
ENV RAILS_ENV ${RAILS_ENV}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
CMD ["bundle", "exec", "rails", "s", "-b", "0.0.0.0", "-p", "8080"]
|
||||
```
|
||||
|
||||
The secret sauce here is `ARG BUNDLE_WITHOUT=test:development`. Running
|
||||
`docker build -t rails-app .` will use the default value provided for the
|
||||
`BUNDLE_WITHOUT` build argument, test:development, and a production Docker image
|
||||
will be built. And if we specify the appropriate build arguments, we can
|
||||
generate an image suitable for development.
|
||||
|
||||
```
|
||||
docker build -t rails-app --build-arg BUNDLE_WITHOUT= --build-arg RAILS_ENV=development .
|
||||
```
|
||||
|
||||
will generate our Docker image with all test and development dependencies
|
||||
available. Typing this for building in development would get pretty tedious so
|
||||
we can use docker-compose to make it easier
|
||||
|
||||
```
|
||||
version: '2'
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- BUNDLE_WITHOUT=
|
||||
- RAILS_ENV=development
|
||||
links:
|
||||
- database
|
||||
ports:
|
||||
- "3000:8080"
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- .:/app
|
||||
tty: true
|
||||
stdin_open: true
|
||||
```
|
||||
|
||||
Now, `docker-compose up -d` is all we need in development to both build and
|
||||
launch our development image.
|
||||
|
||||
Finally, we have a single Dockerfile that can be used to build an image for any
|
||||
of our application’s needs. Of course, there are some trade-offs. For example,
|
||||
build time in development will suffer in some cases. But I have found only
|
||||
maintaining a single Dockerfile to be worth these costs.
|
||||
|
||||
Have another way to deal with this issue? Please share!
|
106
_posts/2017-04-18-write-good-git-commits.md
Normal file
106
_posts/2017-04-18-write-good-git-commits.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
layout: post
|
||||
title: Write Good Git Commits
|
||||
tag:
|
||||
- git
|
||||
- github
|
||||
---
|
||||
|
||||
Writing quality Git commits has been written about to death, but it is so
|
||||
important that I could not help myself from sharing my thoughts on the subject.
|
||||
Mostly, I will be rehashing what
|
||||
[Tim Pope has shared](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
|
||||
but I will also include a few of the things that I have used to improve my
|
||||
source histories.
|
||||
|
||||
### Limit the subject of your commit to 50 characters
|
||||
|
||||
Many people live life as I do: at the command line. Limiting to 50 characters
|
||||
allows room for us to use an 80 character width for commands such as git rebase
|
||||
--interactive without width concerns. Even for those in a more modern IDE, the
|
||||
limit is important.
|
||||
|
||||
![My helpful screenshot](/assets/gui-git.webp)
|
||||
|
||||
As commit messages get longer, it becomes more difficult for everyone to
|
||||
traverse your history.
|
||||
|
||||
### Use the commit body for important details
|
||||
|
||||
It is very rare that I see people extend their commit histories into the body
|
||||
portion of a commit but it is the perfect place to offer extra details. Things
|
||||
like what resources you used to make a specific decision are better left in the
|
||||
commit history than in code comments most of the time. In a
|
||||
[recent commit to my dotfiles](https://github.com/atomaka/dotfiles/commit/28a3897995ff21f63f7893f43582532e4717b8d9),
|
||||
I added a quick alias to correct a TouchBar issue I was having.
|
||||
|
||||
```
|
||||
Fix TouchBar when it (frequently) breaks
|
||||
|
||||
From http://osxdaily.com/2017/01/11/manually-refresh-touch-bar-mac/ this
|
||||
will reset the control portion of the touch bar which has been
|
||||
frequently breaking in New and Interesting Ways™.
|
||||
```
|
||||
|
||||
Without these details, I might never be able to find the source for why I
|
||||
added this.
|
||||
|
||||
Also, limit the line length in the body to 72 characters.
|
||||
|
||||
### Do not use the --message option
|
||||
|
||||
Pretty much everyone I watch make a commit will do so directly at their command
|
||||
prompt with git commit -m. Forcing yourself into an editor will encourage you to
|
||||
add additional details about a commit message. Additionally, your editor can
|
||||
configured to alert you to the 50/72 rule. Simply add au FileType gitcommit set
|
||||
t==72 to your vimrc to auto wrap git messages at 72 characters if vim is
|
||||
configured as your Git editor. [vim-git](https://github.com/tpope/vim-git) is
|
||||
also a fantastic option that will highlight based on these rules.
|
||||
|
||||
If you are struggling with breaking your -m habit, try creating a bash function
|
||||
to prevent its usage.
|
||||
|
||||
```
|
||||
function git {
|
||||
if [[ $@ == "commit -m" ]]; then
|
||||
echo "Do not specify -m for commit"
|
||||
else
|
||||
command git "$@"
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
### Commit as if giving a command
|
||||
|
||||
[How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/)
|
||||
made me consider the subject of my commits a little more. In bullet point 5,
|
||||
Chris says "Use the imperative mood in the subject line." Considering Git itself
|
||||
follows this rule, it made perfect sense to jump on board.
|
||||
|
||||
```
|
||||
Merge branch 'bugfix/correct-spacing'
|
||||
```
|
||||
|
||||
And
|
||||
|
||||
```
|
||||
Revert "Implement person CRUD"
|
||||
```
|
||||
|
||||
The best advice I picked up was write your commit as if you are completing the
|
||||
following sentence: "If applied, this commit will..."
|
||||
|
||||
### Rebase your branches, but leave master alone
|
||||
|
||||
Rebasing is a heated topic in the community but the one thing that everyone can
|
||||
agree on is leaving master alone. Once your commit has made it there, it is
|
||||
history and should never be changed. However, your branch is your story. You get
|
||||
to tell it however you want so craft it in a way that is useful to the reader.
|
||||
|
||||
For me, this is particularly important because the second thing I do when
|
||||
reviewing a new code base is look at the commit history to see to story of the
|
||||
application. Unfortunately, most are riddled with messages like "fixed typo" or
|
||||
"asdf" and the history becomes unreadable.
|
||||
|
||||
Take care in crafting your git messages and you will find much more value in
|
||||
your git logs!
|
4
archive/index.md
Normal file
4
archive/index.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
layout: archive
|
||||
title: Archive
|
||||
---
|
BIN
assets/gui-git.webp
Normal file
BIN
assets/gui-git.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
BIN
atomaka.jpg
Normal file
BIN
atomaka.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
11
build.sh
11
build.sh
|
@ -1,9 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
rm -rf build/
|
||||
mkdir -p build/
|
||||
mdformat --wrap 80 _posts/
|
||||
bundle exec jekyll build
|
||||
|
||||
cp index.html build/
|
||||
cp keybase.txt build/
|
||||
|
||||
AWS_DEFAULT_REGION=us-east-2 aws s3 cp --recursive ./build s3://www.atomaka.com/
|
||||
aws s3 sync _site s3://www.atomaka.com \
|
||||
--size-only \
|
||||
--storage-class REDUCED_REDUNDANCY
|
||||
|
|
BIN
colors.png
BIN
colors.png
Binary file not shown.
Before Width: | Height: | Size: 36 KiB |
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
92
index.html
92
index.html
|
@ -1,92 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Personal Website of Andrew Tomaka</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="description" content="Personal website of Andrew Tomaka">
|
||||
<link rel="canonical" href="http://www.atomaka.com/">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background-color: #4995C7;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
}
|
||||
@media all and (max-width: 480px) {
|
||||
ul {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
color: #07507F;
|
||||
}
|
||||
a {
|
||||
color: #096EB0;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #2988B9;
|
||||
}
|
||||
.container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background-color: #72B0D9;
|
||||
}
|
||||
.content {
|
||||
text-align: center;
|
||||
}
|
||||
.item {
|
||||
max-width: 100%;
|
||||
}
|
||||
.contact {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.color-primary-0 { color: #2980B9 } /* Main Primary color */
|
||||
.color-primary-1 { color: #72B0D9 }
|
||||
.color-primary-2 { color: #4995C7 }
|
||||
.color-primary-3 { color: #096EB0 }
|
||||
.color-primary-4 { color: #07507F }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="contact"><a href="mailto:me@atomaka.com">contact</a></div>
|
||||
<div class="container">
|
||||
<div class="item top"></div>
|
||||
<div class="item content">
|
||||
<div class="title">
|
||||
<h1>Andrew Tomaka</h1>
|
||||
</div>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="https://medium.com/@atomaka">writing</a></li>
|
||||
<li><a href="https://stackoverflow.com/story/atomaka">working</a></li>
|
||||
<li><a href="https://slides.com/atomaka">speaking</a></li>
|
||||
<li><a href="https://github.com/atomaka">coding</a></li>
|
||||
<li><a href="https://twitter.com/atomaka">tweeting</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="item bottom"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
6
index.markdown
Normal file
6
index.markdown
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
# Feel free to add content and custom Front Matter to this file.
|
||||
# To modify the layout, see https://jekyllrb.com/docs/themes/#overriding-theme-defaults
|
||||
|
||||
layout: home
|
||||
---
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
mdformat
|
||||
mdformat-frontmatter
|
Loading…
Reference in a new issue