Blog O' Matty


Debugging a silly node application bug with the inspect interface

This article was posted by Matty on 2017-12-23 11:51:02 -0500 -0500

Last night while working on one of my many side projects I came across a relly weird Javascript issue. Here is an extremely simplified version of the code I was debugging:

$ cat app.js

const myLib = require('./lib.js');
var i = myLib.populateObject("Ollie", "Awesome");
console.log(i);

$ cat lib.js

var populateObject = (name, breed) => {
    var obj = { name: name, breed: breed }
    return 
    {
        obj
    }
}

module.exports = {
    populateObject
}

When the code ran it would return undefined for the object called obj even though console.log() showed it as a valid object inside the function:

$ node app.js

undefined

A seasoned Javascript developer would look at the code above and immediately see the flaw. Being new to javascript it wasn’t immediately clear to me why this wasn’t working. So, I figured this would be as good a time as any to learn how to use the inspect debugger to toubleshoot my issue. The inspect debugger can be accessed by invoking node with the inspect option and the code to run:

$ node inspect app.js

< Debugger listening on ws://127.0.0.1:9229/8d8caf5a-07d1-4043-9418-1acc6935c973
< For help see https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in app.js:1
> 1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
  2 var i = myLib.populateObject("Ollie", "Awesome");
  3 console.log(i);

Once you are inside the debug shell you can use c to continue execution, n to step to the next line, s to step into functions, o to step out of functions and bt to get a backtrace. You can also use setBreakpoint and clearBreakpoint to set and clear breakpoints on specific lines of code and repl to interrogate objects. To see what was going on I stepped into the populateObject function, set a breakpoint on the return line and hit n to see what was run:

debug> s
break in lib.js:2
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
> 2     var obj = { name: name, breed: breed }
  3     return 
  4     {

debug> setBreakpoint(3)
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
  2     var obj = { name: name, breed: breed }
> 3     return 
  4     {
  5         obj
  6     }
  7 }
  8 

debug> c
break in lib.js:3
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
  2     var obj = { name: name, breed: breed }
> 3     return 
  4     {
  5         obj

debug> n
break in lib.js:3
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
  2     var obj = { name: name, breed: breed }
> 3     return 
  4     {
  5         obj

debug> n
break in app.js:3
  1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
  2 var i = myLib.populateObject("Ollie", "Awesome");
> 3 console.log(i);
  4 
  5 });

The return completed but for some reason line 5 wasn’t being evaluated. Then it dawned on me! One of my Javascript instructors mentioned that semicolons were implied if they aren’t specifically specified. So my hypothesis became was there an implicit semicolon added to the return that casued it to return without any values? To see if that was the case I moved the opening brace to be on the same line as the return statement:

var populateObject = (name, breed) => {
    var obj = { name: name, breed: breed }
    return {
        obj
    }
}

module.exports = {
    populateObject
}

And ran my program again. Low and behold it worked! To truly verify this was the issue I re-ran the program under inspect and compared the new output with the previous output I collected:

debug> s
break in lib.js:2
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
> 2     var obj = { name: name, breed: breed }
  3     return {
  4         obj

debug> setBreakpoint(3)
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
  2     var obj = { name: name, breed: breed }
> 3     return {
  4         obj
  5     }
  6 }
  7 
  8 module.exports = {

debug> n
break in lib.js:3
  1 (function (exports, require, module, __filename, __dirname) { var populateObject = (name, breed) => {
  2     var obj = { name: name, breed: breed }
> 3     return {
  4         obj
  5     }

debug> n
break in lib.js:5
* 3     return {
  4         obj
> 5     }
  6 }
  7 

debug> n
break in app.js:3
  1 (function (exports, require, module, __filename, __dirname) { const myLib = require('./lib.js');
  2 var i = myLib.populateObject("Ollie", "Awesome");
> 3 console.log(i);
  4 
  5 });

debug> repl
Press Ctrl + C to leave debug repl
> i
{ obj: Object }
> console.log(i)
< { obj: { name: 'Ollie', breed: 'Awesome' } }

In the output above you can see that we are now reaching line 5 in the program which wasn’t occurring before. This was a silly bug (the result of staying up WAY too late to code) but I learned a TON about the inspect interface in the process. Hopefully as I write more code and study the correct way to do things silly bugs like these will become less prevalent. Viva inspect!

Disabling LLMNR on hosts that use the systemd stub resolver

This article was posted by Matty on 2017-12-22 07:19:26 -0500 -0500

While performing a routine audit of my desktop this morning I noticed that the systemd stub resolver was listening on TCP port 5355:

$ netstat -pant | grep 5355

tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN      2236/systemd-resolv 

TCP port 5355 is used for Link-Local Multicast Name Resolution (LLMNR) which is completely unnecessary for my set up at home. So I ventured off to /etc/systemd and came across the resolved.conf file. While perusing resolved.conf(5) I came across the following two configuration directives:

LLMNR=
Takes a boolean argument or “resolve”. Controls Link-Local Multicast Name Resolution support (RFC 4794[1]) on the local host. If true, enables full LLMNR responder and resolver support. If false, disables both. If set to “resolve”, only resolution support is enabled, but responding is disabled. Note that systemd-networkd.service(8) also maintains per-link LLMNR settings. LLMNR will be enabled on a link only if the per-link and the global setting is on.

MulticastDNS=
Takes a boolean argument or “resolve”. Controls Multicast DNS support (RFC 6762[2]) on the local host. If true, enables full Multicast DNS responder and resolver support. If false, disables both. If set to “resolve”, only resolution support is enabled, but responding is disabled. Note that systemd- networkd.service(8) also maintains per-link Multicast DNS settings. Multicast DNS will be enabled on a link only if the per-link and the global setting is on.

Setting both values to no and bouncing the systemd-resolved.service service stopped systemd-resolv from listening on *:5355. If you are interested in learning more about LLMNR you can check out RFC 4795. This helped me clarify a number of questions I had about the protocol and when to use it.

Amazing tool for visualizing regular expression evaluation

This article was posted by Matty on 2017-12-20 06:48:20 -0500 -0500

Like most admins I use regular expressions pretty much every day. When I’m writing more complex expressions I’ve always wanted to visually observe what is actually happening. Well, to my surprise someone came up with an incredible online tool to do just that:

Online regular expression evaluation website

This is definitely one I will be adding to my bookmark bar!

Managing Vsphere From The Linux Command Line

This article was posted by Matty on 2017-12-19 09:35:40 -0500 -0500

For the past year I’ve been working quite a bit with VMWare’s vSphere virtualization suite. Managing vSphere through the web UI has been extremely frustrating though. The bugs, constant crashes and sluggishness have really impeded my ability to be productive. Being a command line user who loves to automate I decided to test out the govc command line utility to see if this would improve my experience. After a good deal of use I can affirmatively say that this gem makes a HUGE difference in productivity!

To get going with govc all you need is a Linux machine. The govc binary is opensource and the build team provides binary builds on their release page. Installing it is easy:

$ wget https://github.com/vmware/govmomi/releases/download/v0.16.0/govc_linux_amd64.gz

$ gunzip govc_linux_amd64.gz && mv govc_linux_amd64 /usr/local/bin/govc && chmod 755 /usr/local/bin/govc

If you want to live on the cutting edge you can also build govc from source:

$ mkdir govc

$ export GOPATH=/home/matty/govc

$ cd govc

$ go get -u github.com/vmware/govmomi/govc

$ cp bin/govc /usr/local/bin/govc && chmod 755 /usr/local/bin/govc

Once installed you can verify the binary works by running it with the about command:

$ govc about

Name:         VMware vCenter Server
Vendor:       VMware, Inc.
Version:      6.5.0
Build:        7119157
OS type:      linux-x64
API type:     VirtualCenter
API version:  6.5
Product ID:   vpx
UUID:         93c8b2a5-3715-4a27-8a3a-4758848ddce0

To list all of the commands govc can be run without an argument:

$ govc

Usage of govc:
  about
  about.cert
  cluster.add
  cluster.change
  cluster.create
  datacenter.create
  datacenter.info
  datastore.cp
  .....

To connect govc to a vCenter instance you will need to export a couple of variables (if you specify a root CA certificate bundle with the GOVC_TLS_CA_CERTS option you can leave out the first export):

$ export GOVC_INSECURE=true

$ export GOVC_URL='https://USER:PASSWORD@vcenter.prefetch.net/sdk'

$ export GOVC_DATACENTER=DC

To see the true utility of govc let me show a couple of examples. Govc takes a command as the first argument followed by one or more options. The following examples show how to list all of your vCenter objects and retrieve information about your data center:

$ govc ls

/DC/vm
/DC/network
/DC/host
/DC/datastore

$ govc datacenter.info

Name:                DC
  Path:              /DC
  Hosts:             2
  Clusters:          1
  Virtual Machines:  2
  Networks:          7
  Datastores:        6

To view all of your data stores you can use the datastore.info command:

$ govc datastore.info

Name:        ds01
  Path:      /DC/datastore/ds01
  Type:      VMFS
  URL:       ds:///vmfs/volumes/586a9fb0-4b44f5b6-942c-001b21b97588/
  Capacity:  225.2 GB
  Free:      224.3 GB

To find an object you can use the find command (this is super handy):

$ govc find vm -name *kub*

vm/kub1

To get information about an ESXi host you can pass the name of the host to the host.info command:

$ govc host.info esx01.homefetch.net

Name:              esx01
  Path:            /DC/host/ProdCluster/esx01
  Manufacturer:    MSI
  Logical CPUs:    4 CPUs @ 3192MHz
  Processor type:  Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz
  CPU usage:       182 MHz (1.4%)
  Memory:          65411MB
  Memory usage:    14433 MB (22.1%)
  Boot time:       2017-12-19 13:23:03.394348 +0000 UTC
  State:           connected

To retrieve all of the information about a VM you can use the vm.info command:

$ govc vm.info kub1

Name:           kub1
  Path:         /DC/vm/kub1
  UUID:         420fd911-0f20-0898-34ed-2b055519a54e
  Guest name:   Red Hat Enterprise Linux 7 (64-bit)
  Memory:       4096MB
  CPU:          1 vCPU(s)
  Power state:  poweredOn
  Boot time:    2017-12-19 13:19:08.926664 +0000 UTC
  IP address:   192.168.1.213
  Host:         esx02

Govc also provides access to the host and VM metrics. You can get a list of metrics for an object with the metric.ls command:

$ govc metric.ls vm/kub1

cpu.usagemhz.average
cpu.run.summation
cpu.overlap.summation
cpu.ready.summation
cpu.demand.average
cpu.readiness.average
cpu.entitlement.latest

To collect a sample you can use the metric.sample command:

$ while :; do govc metric.sample vm/kub1 cpu.usagemhz.average; sleep 1; done

kub1  -  cpu.usagemhz.average    1777,17,8,17,11,9  MHz
kub1  0  cpu.usagemhz.average    1774,5,4,4,4,4     MHz
kub1  -  cpu.usagemhz.average    1777,17,8,17,11,9  MHz
kub1  0  cpu.usagemhz.average    1774,5,4,4,4,4     MHz

In addition to the commands listed above I’ve found the following just as useful (you can get a full list of commands and their options on the govc github page):

datastore.upload - upload object to a data store
dvs.portgroup.info - get DVS port group info
events - view events
folder.create - create a folder
host.maintenance.enter - put a host into maintenance mode
host.maintenance.exit - take a host out of maintenance mode
logs - view specific logs
snapshot.create - create a snapshot
snapshot.remove - delete a snapshot
tasks - view vCenter tasks
vm.power - power a VM on and off

Huge thanks to the govc team as well as the maintainers of the govmomi library!

Adding a new Operating System to the cobbler signature file

This article was posted by Matty on 2017-12-16 10:07:01 -0500 -0500

This has been an exciting week of code analysis and debugging. As I mentioned previously cobbler doesn’t currently have support for Fedora 27. It it turns out adding a new Operating System signature is super easy. For my specific situation I forked the cobbler git repo and copied the Fedora 26 signature line and changed the 6 to a 7. Once I updated the signature file and restarted cobblerd everything imported without issue:

$ cobbler import --name=fedora27 --arch=x86_64 --path=/mnt

task started: 2017-12-16_095536_import
task started (id=Media import, time=Sat Dec 16 09:55:36 2017)
Found a candidate signature: breed=redhat, version=rhel6
Found a candidate signature: breed=redhat, version=rhel7
Found a candidate signature: breed=redhat, version=fedora21
Found a candidate signature: breed=redhat, version=fedora20
Found a candidate signature: breed=redhat, version=fedora23
Found a candidate signature: breed=redhat, version=fedora22
Found a candidate signature: breed=redhat, version=fedora25
Found a candidate signature: breed=redhat, version=fedora24
Found a candidate signature: breed=redhat, version=fedora27
Found a matching signature: breed=redhat, version=fedora27
Adding distros from path /var/www/cobbler/ks_mirror/fedora27-x86_64:
creating new distro: fedora27-x86_64
trying symlink: /var/www/cobbler/ks_mirror/fedora27-x86_64 -> /var/www/cobbler/links/fedora27-x86_64
creating new profile: fedora27-x86_64
associating repos
checking for rsync repo(s)
checking for rhn repo(s)
checking for yum repo(s)
starting descent into /var/www/cobbler/ks_mirror/fedora27-x86_64 for fedora27-x86_64
processing repo at : /var/www/cobbler/ks_mirror/fedora27-x86_64
need to process repo/comps: /var/www/cobbler/ks_mirror/fedora27-x86_64
looking for /var/www/cobbler/ks_mirror/fedora27-x86_64/repodata/*comps*.xml
Keeping repodata as-is :/var/www/cobbler/ks_mirror/fedora27-x86_64/repodata
*** TASK COMPLETE ***

Now back to our regularly scheduled programming. Time for some eBPF testing!