Wednesday, 18 March 2020

IP calculations in BASH with bitwise operations

This simple BASH script uses a network CIDR and two reserved IPs to calculate the pool range for a DHCP configuration. The two reserved IPs can be any valid IP within the CIDR. The IP range with the largest number of available IP, not using one of the static IPs, is selected for use as the pool. The highest and lowest IPs of the pool are returned.

The script uses ipcalc for getting the network values from the network CIDR. It is able to get values for Network, Netmask, Address, Broadcast, HostMin, HostMax and the number of host addresses in the network.

Unit conversion from decimal to binary is done using bc. BASH is able to perform bitwise operations for AND, OR and XOR. The BASH operation of shift is also used in this script.

As always you can download this code along with all the others from this blog using GitLab.com Linux Code Snippets


#!/bin/bash

# Given the network CIDR and two static IPs, calculate the lower and upper IPs for the DHCP pool.
# This script uses ipcalc that may need to be installed.


# https://stackoverflow.com/questions/40667382/how-to-perform-bitwise-operations-on-hexadecimal-numbers-in-bash
# https://unix.stackexchange.com/questions/223338/convert-a-value-into-a-binary-number-in-a-shell-script
# echo "obase=2; 34" | bc

# https://unix.stackexchange.com/questions/65280/binary-to-hexadecimal-and-decimal-in-a-shell-script
# echo $((2#101010101))
# printf "%x\n" "$((2#101010101))"
# echo $((0xf8 ^ 0x1f)) XOR
# echo $((0xf8 & 0x1f)) AND
# echo $((0xf8 | 0x1f)) OR
# echo $((0xf8 >> 3)) shift right 3 bits
# echo $((0xf8 << 4)) shift left 3 bits

function AddOne {
   IPCI=
   [ "$1" = "NS_ADDR" ] && IPCI=$(( NS_ADDR_INT + 1 ))
   [ "$1" = "GW_ADDR" ] && IPCI=$(( GW_ADDR_INT + 1 ))
   IPCB=$(echo "obase=2; $IPCI" | bc)
   Bin2Addr $(( 2#$B_ADDRESS_MASK | 2#$IPCB ))
}

function SubOne {
   IPCI=
   [ "$1" = "NS_ADDR" ] && IPCI=$(( NS_ADDR_INT - 1 ))
   [ "$1" = "GW_ADDR" ] && IPCI=$(( GW_ADDR_INT - 1 ))
   IPCB=$(echo "obase=2; $IPCI" | bc)
   Bin2Addr $(( 2#$B_ADDRESS_MASK | 2#$IPCB ))
}

function Bin2Addr {
   GA=$(echo "obase=2; $1" | bc)
   D_BLOCK=$(( 2#$GA & 2#11111111 ))
   GA=$( echo "obase=2; $(( 2#$GA >> 8 ))" | bc)
   C_BLOCK=$(( 2#$GA & 2#11111111 ))
   GA=$( echo "obase=2; $(( 2#$GA >> 8 ))" | bc)
   B_BLOCK=$(( 2#$GA & 2#11111111 ))
   GA=$( echo "obase=2; $(( 2#$GA >> 8 ))" | bc)
   A_BLOCK=$(( 2#$GA & 2#11111111 ))
   echo "built IP = '${A_BLOCK}.${B_BLOCK}.${C_BLOCK}.${D_BLOCK}'"
}

function NetCalcs {
   echo
   echo --------------------------------------------------
   # set -x
   echo "NAMESERVER_IP=$NAMESERVER_IP, GATEWAY_IP=$GATEWAY_IP"
   NAMESERVER_NET=$(ipcalc -nb ${NETWORK_CIDR} | grep ^Network: | awk '{print $2}' )
   #echo "NAMESERVER_NET=$NAMESERVER_NET"
   NETWORK_MASK=$(ipcalc -nb ${NETWORK_CIDR} | grep ^Netmask: | awk '{print $2}' )
   #echo "NETWORK_MASK=$NETWORK_MASK"
   B_ADDRESS_MASK=$(ipcalc -n ${NETWORK_CIDR} | grep ^Address: | sed -e 's/\.//g' | awk '{print $(NF-1)$NF}' )
   #echo "Binary ADDRESS_MASK=$B_ADDRESS_MASK"
   NETWORK_BROADCAST=$(ipcalc -nb ${NETWORK_CIDR} | grep ^Broadcast: | awk '{print $2}' )
   #echo "NETWORK_BROADCAST=$NETWORK_BROADCAST"
   NET_MIN_IP=$(ipcalc -nb ${NETWORK_CIDR} | grep ^HostMin: | awk '{print $2}' )
   #echo "NET_MIN_IP=$NET_MIN_IP"
   NET_MAX_IP=$(ipcalc -nb ${NETWORK_CIDR} | grep ^HostMax: | awk '{print $2}' )
   #echo "NET_MAX_IP=$NET_MAX_IP"
   NET_MIN_BIN=$(ipcalc -n ${NETWORK_CIDR} | grep ^HostMin: | sed -e 's/\.//g' | awk '{print $NF}')
   #echo "NET_MIN_BIN=$NET_MIN_BIN"
   NET_MAX_BIN=$(ipcalc -n ${NETWORK_CIDR} | grep ^HostMax: | sed -e 's/\.//g' | awk '{print $NF}')
   #echo "NET_MAX_BIN=$NET_MAX_BIN"
   NS_ADDR_BIN=$(ipcalc -n ${NAMESERVER_IP}/${NETWORK_CIDR##*/} | grep ^Address: | sed -e 's/\.//g' | awk '{print $NF}')
   NS_ADDR_INT=$(( 2#$NS_ADDR_BIN ))
   #echo "NS_ADDR_BIN=$NS_ADDR_BIN"
   GW_ADDR_BIN=$(ipcalc -n ${GATEWAY_IP}/${NETWORK_CIDR##*/} | grep ^Address: | sed -e 's/\.//g' | awk '{print $NF}')
   GW_ADDR_INT=$(( 2#$GW_ADDR_BIN ))
   #echo "GW_ADDR_BIN=$GW_ADDR_BIN"

   read LOWEST HIGHEST <<< $( printf "%d " $(printf "%d\n" $(( 2#$GW_ADDR_BIN )) $(( 2#$NS_ADDR_BIN ))|sort -n))
   LOWCOUNT=$(( LOWEST - $NET_MIN_BIN ))
   #echo "LOWCOUNT=$LOWCOUNT"

   # Mid IP pool size
   MIDCOUNT=$(( HIGHEST - LOWEST ))
   #echo "MIDCOUNT=$MIDCOUNT"

   # Upper IP pool size
   HICOUNT=$(( 2#$NET_MAX_BIN - HIGHEST ))
   #echo "HICOUNT=$HICOUNT"


   read JUNK LOWNET JUNK HIGHNET JUNK <<< $( echo $( (echo "$GW_ADDR_INT GW_ADDR"; echo "$NS_ADDR_INT NS_ADDR"; ) | sort -n) )
   read JUNK POOL JUNK <<< $( (echo "$LOWCOUNT LOWCOUNT"; echo "$MIDCOUNT MIDCOUNT"; echo "$HICOUNT HICOUNT";) | sort -nr)


   #echo "NAMESER_IP=$NAMESERVER_IP GATEWAY_IP=$GATEWAY_IP LOWNET=$LOWNET HIGHNET=$HIGHNET POOL=$POOL"

   case $POOL in
      LOWCOUNT)
         echo "From $NET_MIN_IP to $(SubOne $LOWNET)";;
      MIDCOUNT)
         echo "From $(AddOne $LOWNET) to $(SubOne $HIGHNET)";;
      HICOUNT)
         echo "From $(AddOne $HIGHNET) to $NET_MAX_IP";;
   esac

}


clear
NETWORK_CIDR="172.16.0.0/20"
echo "NETWORK_CIDR=$NETWORK_CIDR"

NAMESERVER_IP="172.16.1.2"
GATEWAY_IP="172.16.1.1"
NetCalcs


NAMESERVER_IP="172.16.14.200"
GATEWAY_IP="172.16.15.9"
NetCalcs


NAMESERVER_IP="172.16.1.22"
GATEWAY_IP="172.16.1.22"
NetCalcs


NETWORK_CIDR="192.168.1.0/24"
echo "NETWORK_CIDR=$NETWORK_CIDR"

NAMESERVER_IP="192.168.1.2"
GATEWAY_IP="192.168.1.1"
NetCalcs


NAMESERVER_IP="192.168.1.120"
GATEWAY_IP="192.168.1.121"
NetCalcs


NAMESERVER_IP="192.168.1.2"
GATEWAY_IP="192.168.1.1"
NetCalcs

Sunday, 16 February 2020

Using Jq to work with JSON

While starting to work with Packer & Terraform, our deployment server needed some information about the AMIs that are being built. This sounds like a job for jq


#!/bin/bash

TMP=$(mktemp /dev/shm/JQ_XXXXXXXXXXXXXXX)
echo $TMP

echo Create a new JSON data set.
sleep 1
jq -Rn '{ "firewall": { amiId: "ami-1234", base: "amazon-linux-2", built: "'$(date +%F_%T)'" }}' > $TMP
cat $TMP | jq
sleep 5

echo Append to a JSON data set.
sleep 1
cat $TMP | jq '.["webserver"].amiId="ami-5678"' > ${TMP}.swap && mv ${TMP}.swap $TMP
cat $TMP | jq '.["webserver"].base="centos-7"' > ${TMP}.swap && mv ${TMP}.swap $TMP
cat $TMP | jq
sleep 5

echo Get all values for an entry by name
sleep 1
cat $TMP | jq '.["firewall"]'
sleep 5

echo Get the amiId by name
sleep 1
cat $TMP | jq '.["firewall"].amiId'
sleep 5

echo Set the amiId by name
sleep 1
cat $TMP | jq '.["firewall"].amiId="AMI-9876"' # > ${TMP}.swap && mv swap $TMP
sleep 5

echo Select a data set by exact match
sleep 1
cat $TMP | jq 'with_entries(select(.value.base=="centos-7"))'
sleep 5

echo Select a data set by partial match
sleep 1
cat $TMP | jq 'with_entries(select(.value.base | startswith("amazon")))'
sleep 5

rm -f $TMP


Create a new JSON data set.









Append to a JSON data set.












Get all values for an entry by name.







Get the amiId by name




Set the amiId by name













Select a data set by exact match







Select a data set by partial match


























Tuesday, 11 February 2020

The One Line Python Directory Web Server

While testing some adjustments to iptables I needed a simple way to test web requests. This is a handy way to serve a directory and a simple way to see if the firewall is working.

On the server-side create directory content that will be hosted from a one-line Python script.
mkdir /tmp/fakeserver

cd /tmp/fakeserver

git clone https://github.com/torvalds/linux.git

sudo python -m SimpleHTTPServer 80


On the client-side use wget to spider and download the entire content of the server.
Replace <TEST SERVER> with the servers IP or resolvable name.
mkdir /tmp/fakeserver

cd /tmp/fakeserver

wget --mirror --convert-links --adjust-extension \
--page-requisites --no-parent http://<TEST SERVER>/



Sunday, 8 September 2019

nmon on your headless Proxmox server

Proxmox is great and after it is up and running you can control it from the web page or ssh in, so what do you do with the monitor connected to it. Here is a one-line command to get nmon to show you what the server is doing.

I set my script up in cron so it always comes up, also nmon will not refresh the entire screen so cron can also kill the old nmon and start a new one.

I called this headless because you don't need to login to have this work. The pgrep command finds the getty that has the login prompt.



( sleep 2; 
export NMON=cmVdn; 
export TERM=xterm-color; 
export NCURSES_NO_UTF8_ACS=1; 
nmon > /proc/$(pgrep -f agetty)/fd/1 ) &

And this is what it looks like.


Thursday, 6 June 2019

The Perl Negative Modulus Bug

This is a bug that has been in Perl for all the years I have used it. There is a workaround, but I choose to avoid using Perl for anything beyond simple math.

The bug only shows when you try to calculate the modulus of negative values.


#!/usr/bin/perl

$A = 106;
printf("Division\n");
printf("106 / 10 = 10\t<- expected\n");
printf("%d / 10 = %01d\t<- correct\n", $A, $A / 10);

printf("\n");
printf("Modulus\n");
printf("106 %% 10 = 6\t<- expected\n");
printf("%d %% 10 = %d\t<- correct\n", $A,  $A % 10);

printf("\n\nNegative values\n");

$A = -106;
printf("Division\n");
printf("%d / 10 = 10\t<- expected\n", $A);
printf("%d / 10 = %01d\t<- correct\n", $A, $A / 10);

printf("\n");
printf("Modulus\n");
printf("%d %% 10 = -6\t<- expected\n", $A);
printf("%d %% 10 = %d\t<- WHAT THE!!!\n", $A,  $A % 10);

This is the output.

Division
106 / 10 = 10 <- expected
106 / 10 = 10 <- correct

Modulus
106 % 10 = 6 <- expected
106 % 10 = 6 <- correct


Negative values
Divsion
-106 / 10 = 10 <- expected
-106 / 10 = -10 <- correct

Modulus
-106 % 10 = -6 <- expected
-106 % 10 = 4 <- WHAT THE!!!


Friday, 24 May 2019

Simple Docker Example

This is a continuation of the Ultra minimal docker Node.JS example.

The code can be downloaded from GitLab.com

This is a very simple example of Docker running a simple NodeJS app.

Things to know. Containers are not VMs, they are more like a root jail. They use the host OS kernel and processes running in the container are visible to the host OS. Docker is just one of many ways to use containers. Proxmox is another, and it can also run VMs.

This example requires a Linux system with Docker installed. Installing Docker for Debian Installing Docker for CentOS

For more information see this cheat sheet.

Almost all of these steps require permission to execute privileged commands. In a production environment, a special group would be created with permissions to run these commands. If you are new to Linux the sudo command is commonly used to run commands as the root administrator, just prefix the commands with sudo. It is recommended that you practice this on a test system.

Image Files

Image files are a collection of Linux OS files needed to run in a container. They contain a directory tree like /bin/, /lib/, /usr/, /var/ and others, everything needed to run the required programs.

One popular image is BusyBox were hundreds of Linux programs such as ls, grep, find, cat are all just one binary program. This makes the image very small.

This sample project uses alpine-node, a small image with a functional NodeJS.

Building A Simple Project

Three files are included with this project.

service.js

This is a simple JavaScript to be run by NodeJS

package.json

This file tells NodeJS about the code to run.
It tells NodeJS:
  • the name of the script to start
  • dependencies that are needed
  • how to start the script

Dockerfile

This tells Docker how to build an image that contains your project.
It tells Docker
  • what image to source the build from, this one uses mhart/alpine-node
  • where to install code files
The source image mhart/alpine-node is a very small Linux that includes a NodeJS service.

Building Images

On a Linux system where you have a running Docker, change to the working code directory where the Dockerfile is, then build a new image.

docker build -t <new image title> .

This will copy the mhart/alpine-node image and add the project code to it as instructed by the contents of the Dockerfile.

If your docker does not have the mhart/alpine-node image yet, the build will automatically download it. This may take extra time but after it has been downloaded, future builds will be much faster.

Running Containers

Once the image is built, it is just an image. It is not yet an instance of a container, for that it needs to be run. The container is created and initialized when it is first run.

Then run a container with this new image, this will create a new container.
docker run -d -p 3000:3000 <new image title>

The -p 3000:3000 will publish the service at port 3000 inside the container on the host OS.

The -d sets the container to run detached so it will fork to the background.


Monitoring

At this point, the docker service is running and should respond to web requests.
Open a browser to the IP address of the Linux host OS at port 3000
http://<linux>:3000

The node process can be seen running in the host OS.
From your host Linux system, run these commands.
ps -ef | grep node
netstat -naptu |grep 3000.*LISTEN

List the status of running containers

docker ps 
docker container ls

List the status of all containers.

docker ps -a
docker container ls -a

Status

Get a top like status of the running containers.
docker stats

To just get a one time report of stats.
docker stats --no-stream

Stopping Containers

Containers must be stopped before they can be deleted.
docker stop <container ID>

Re-Starting Containers

Once a container has been ran, it has been initialized and can be restarted.
docker start <container ID>

Deleting Containers

List the containers, use -a all to see the ones that are not running.
docker container ls -a

Containers must be deleted before the associated image can be deleted.
docker rm <container ID>

Deleting Images

First, list the images.
docker image ls

Then use the image name or ID to delete it.

docker rmi <image name or ID>

Monday, 4 February 2019

Get your data together!

Analyzing logs is fun, kind of like how going to the dentists is fun. You stair at lon-n-n-n-g pages of data and your brain goes numb from the overwhelming amount of information.

GBT is a Perl script that can condense and extract meaningful information from time-indexed logs and numbers. GBT is Group-By-Time and rather than have sporadic bursts of data separated by black holes of emptiness, GBT produces a consistent flow of data that is easily graphable 

GBT by default will condense data into ten minute blocks of maximum values for each column given. The size of the time block can be changed using the -t argument. The output data format can be changed to provide min, max, mean, sum, delta or count. 

The main use for GBT is to pipe data into GNU Plot to produce graphs.


Here is the gnuplot PNG created from the sample data.

Wednesday, 30 January 2019

Ooooh that shell, Can't you shell that shell

Staying up late to work on a fun project. Building a custom PXE boot image that will be used to pre-load our servers and get them ready for Puppet management. It has been about eight years since I had built a custom boot image. At that time busybox was my goto for small, powerful environments that needed to run from very small RAM mounted file systems.
BusyBox combines tiny versions of many common UNIX utilities into a single small executable. It provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. BusyBox provides a fairly complete environment for any small or embedded system.
When most distros offer boot images that do little more than boot, or fail to boot; for my systems I used busybox to create a very compact environment that would allow you to perform diagnostics and fix any problems.

As great as busybox was at the time, I found it lacked a lot of compatibilities with even old POSIX systems and scripts. Arguments that you rely on for find, grep and even ls, just were not there. Things have changed; somewhat. Busybox has grown up and is full of new abilities.

Screenshot of "make menuconf" providing the ability
to choose the features to build into the binary.


Wednesday, 12 December 2018

The ancestry of a process

When it comes to viewing the heritage of a process, the go-to command is pstree. By itself, pstree will show the descendant processes. Using the -s argument it will show ancestor processes. A good trick to remember is slap, pstree -slap <PID>.

I personally like the format of ps and being the kind of person that likes to come up with one line tools, I created this little snippet.



{ TPID=$$; while [[ $TPID -ne 1 ]]; do ps -f $TPID | tail -n1; TPID=$(ps h -o ppid -p $TPID); done; ps -f $TPID|tac; } | tac

Saving it to use later we get this.



#!/bin/bash

{ 
        TPID=${1:-$$}; 
        while [[ $TPID -ne 1 ]]; do 
                ps -f $TPID | tail -n1; 
                TPID=$(ps h -o ppid -p $TPID); 
        done; 
        ps -f $TPID | tac; 
} | tac

This is what the two commands output.


Monday, 5 November 2018

Jget, a simple CLI web client for testing

“Knock, knock.”
“Who’s there?”
very long pause….
“Java.”

I made this to test the way Java makes web requests. It is both simple and inefficient at being a web client.



/*  Compile
 *  javac Jget.java
 *
 *  Testing
 *  java Jget http://google.com
 */

import java.io.*; 
import java.net.*;  

public class Jget {  
 public static String getHTML(String urlToRead) throws Exception { 
  StringBuilder result = new StringBuilder(); 
  URL url = new URL(urlToRead); 
  HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
  conn.setRequestMethod("GET"); 
  BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); 
  String line; 
  while ((line = rd.readLine()) != null) { 
   result.append(line); 
  } 
  rd.close(); 
  return result.toString(); 
 }  

 public static void main(String[] args) throws Exception { 
  System.out.println(getHTML(args[0])); 
 } 
}

Wednesday, 17 October 2018

Expandable Disk Server

In my SOHO I have tried many types of storage solutions. Currently the system is a Qnap, four bay system. It's turn key, user friendly approach does make it easy to setup and use, but it's highly limited with not having a fifth drive bay. Four bays allows for two disks in RAID-1 or one big disk in RAID-10, not having a fifth bay prevents upgrading to larger disks, without fronting the cash to buy an entire new system and disks all at once.

It's time for a storage solution that can grow over time, not only to replace smaller disk but to allow for the system to start with just one disk and add to the cluster over the next few month.

Looking at using FreeNAS to manage a NAS server built from a Linux box. This will not be a dedicated box, so I will be building a Proxmox and hosting the FreeNAS as a VM inside the Proxmox. The FreeNAS needs to grow as new disks are added so it will be using the disks as an NFS share from Proxmox.

The plan is to use Wester Digital 12TB disks, as these disks are almost $700 each, only one disk will be added to the system every two weeks over the next two months.

Building the disk array will use Linux software RAID-1, LVM, and XFS.

RAID-1
This level of RAID is for redundancy, all disks in the RAID are kept identical. When one disk fails the file system continues to work.

LVM
LVM visualises the block layer of block devices. This is a powerful tool for manipulating block devices on a running system. LVM does have builtin support for mirroring and RAID, including RAID-1 and RAID-10, but happens at the logical volume layer and using these would prevent the migration to larger disks.

XFS
This file system allows for growing live without the need to format or reboot the system.

To test this plan, and demonstrate how this will work, VMware running Fedora 27 was used. The images used a 8GB SCSI for the root file system. The disks in the cluster are SATA and are marked a through f.

In this example the commands used create Physical Volumes with names that match the RAID-1 device, while the images show the Physical Volumes with PV names.

Step 1 Create a new RAID-1
mdadm --create /dev/md0 --run --level=1 --raid-devices=2 /dev/sda missing




Step 2 Create a new physical volume
pvcreate /dev/md0

Step 3 Create a new volume group
vgcreate vg0 /dev/md0

Step 4 Create a new logical volume
lvcreate -l 100%VG -n lv0 vg0

Step 5 Create the XFS
mkfs.xfs /dev/vg0/lv0
mkdir /xfs0
mount /dev/vg0/lv0 /xfs0/

Step 6 Add another disk to the RAID
mdadm --add /dev/md0 /dev/sdb

Step 7 Create a new RAID-1
mdadm --create /dev/md1 --run --level=1 --raid-devices=2 /dev/sdc missing

Step 8 Create a new physical volume
pvcreate /dev/md1

Step 9 Grow the existing volume group
vgextend vg0 /dev/md1

Step 10 Grow the existing logical volume
lvextend -l 100%VG vg0/lv0

Step 11 Grow the existing XFS live
xfs_growfs /xfs0/
Add the last disk the same way as Step 6.



Expand the disk cluster by adding new PVs and migrating

Step 12 Create the new RAID and PV
mdadm --create /dev/md2 --run --level=1 --raid-devices=2 /dev/sd/ missing
pvcreate /dev/md2

Step 13 Extend the volume group to include the new PV
vgextend vg0 /dev/md2

Step 14 Move all blocks from the old PV to the new PV.
pvmove /dev/md0 /dev/md2

Step 15 Remove the first PV.
vgreduce vg0 /dev/md0

Step 16 Grow the logial volume again
lvextend -l 100%VG vg0/lv0

Step 17 Re-grow the existing XFS live
xfs_growfs /xfs0/

Saturday, 6 October 2018

Now on GitLab.com

You can now download all the code from GitLab.com

git clone git@gitlab.com:SiliconTao-open-source/linux-code-snippets.git

Host your projects on GitLab.com

Wednesday, 12 September 2018

Sed: Append file to end of line

Sed can let you replace part of a line with new content from another file. This is how I do this in three steps. You can see the code run a codingground

By using a file to insert the replacement text the content can contain special characters that would normally require a lot of extra work to escape. 


#!/bin/bash

# Replace the right portion of a line with the content of a file.
# 1) Replace the right portion of a line with a marker word
# 2) Append after the marker with the content from a file
# 3) Remove the marker and new line to pull the next line up.

cat > outfile <<!EOF
hello there tom
        How are you today?
    Where is the dog show?
!EOF

cat outfile

cat > testfile <<!EOF
flowers doing?
!EOF


sed -i outfile -e 's/you.*/REPLACE_MARKER/g'
printf "\n\n\n"
cat outfile

sed -i outfile -e "/REPLACE_MARKER/r testfile"
printf "\n\n\n"
cat outfile

sed -i outfile -e "/REPLACE_MARKER/{:a;N;s/REPLACE_MARKER\n//}"
printf "\n\n\n"
cat outfile

Tuesday, 24 July 2018

Ultra minimal docker Node.JS example

This is how to get a minimal docker image with just node and run a simple service.
Docker will automatically download the image for alpine-node if you do not already have it.
Use the run command to create and start the new container.
Use -p to publish the container port 3000 on the host at port 3000 on the IP address of the host.
Use -it to connect interactively to the TTY so you can enter and run commands

docker run -p 3000:3000 -it mhart/alpine-node

Your command line is now inside the docker container.
Edit the code of the service.
Copy and paste the sample code below and save the file.

vi service.js

Run node on the service.

node service.js

Open your web browser to the IP address of your host system using :3000 to set the request port.
You should see the hello message.
Use Ctrl+c to exit node & Ctrl+d to exit the container.

You now have a functional container that is set up to run this simple script.
Use the ls --all command to see the container you built.

docker container ls --all

Use the start command to start the container up again.
Use the container ID you found from the ls command.

docker container start <CONTAINER ID>

Check that the container is running and publishing port 3000.

docker container ls

Use the attach command to re-attach to the container.

docker container attach

Run the node command again.

node service.js




var http = require('http');

var server = http.createServer( function(req, res) {
 if (req.url === '/') {
  res.write("Hello World!");
  res.write("I have been expecting you.");
  res.end();
 }

 if (req.url === '/snarky') {
  res.write("If you code it, it will crash!");
  res.end(); 
 }

});

// Port 3000 on IPv4
server.listen(3000, "0.0.0.0");

Thursday, 12 July 2018

BASH date picker

Sometimes I need users to pick a date from within a bash shell but I need the format to be correct. This little script lets users pick a date and it will output in whatever format the script needs.

Users can use the arrow keys to move the date on the calendar and press enter to select the date.


#!/bin/bash

DONE=0
DATE=$(date +%F)
YEAR="$(echo $DATE | cut -b1-4)"
MONTH="$(echo $DATE | cut -b6-7)"
DAY="$(echo $DATE | cut -b9-10 | sed -e 's/^0/ /')"
while [ $DONE -eq 0 ]; do
 clear
 cal -h ${MONTH} ${YEAR} | GREP_COLOR='47;30' grep "$DAY" -wC6 --color=always
 read -rsn1 KEY
 [[ $KEY == $'\x1b' ]] && { 
  read -rsn1 -t 0.1 DC
  read -rsn1 -t 0.1 KP
  case $KP in
   "A") DATE=$(date +%F -d "$DATE 7 day ago");;
   "B") DATE=$(date +%F -d "$DATE 7 day");;
   "C") DATE=$(date +%F -d "$DATE 1 day");;
   "D") DATE=$(date +%F -d "$DATE 1 day ago");;
  esac
 } || { 
  DONE=1
 }
 
 YEAR="$(echo $DATE | cut -b1-4)"
 MONTH="$(echo $DATE | cut -b6-7)"
 DAY="$(echo $DATE | cut -b9-10 | sed -e 's/^0/ /')"
done
echo $DATE

# To change the output format use the date command like these examples do
# date +%d/%m/%Y -d "$DATE"
# date -d "$DATE"