How to Visualize Prometheus TSDB With Thanos

Have you ever tried to ls the Prometheus TSDB data directory just to understand what kind of data you have from the perspective of time? Here is an example listing:

drwxrwxr-x  3 gstatkevicius gstatkevicius  4096 Jun 26  2020 01EBRP7DA9N67RTT9AFE0KMXFJ
drwxrwxr-x  3 gstatkevicius gstatkevicius  4096 Jun 26  2020 01EBRP7DCM68X5SPQGK3T8NNER
drwxrwxr-x  2 gstatkevicius gstatkevicius  4096 Jun 26  2020 chunks_head
-rw-r--r--  1 gstatkevicius gstatkevicius     0 Apr 17  2020 lock
-rw-r--r--  1 gstatkevicius gstatkevicius 20001 Jan  2 14:01 queries.active
drwxr-xr-x  3 gstatkevicius gstatkevicius  4096 Jan  2 14:01 wal

We could assume that each block is 2 hours long. However, they can be compacted i.e. “joined” into bigger ones if that is enabled. Then, the blocks would not line up in a sequential way. Plus, this output of ls doesn’t even show the creation time of different blocks. We could theoretically get those timestamps with stat(2), statx(2) and some shell magic but that’s cumbersome. Overall, this doesn’t seem too helpful and we can do better.

Enter Thanos. I will not go over what it is in detail in this article to keep it terse however I am going to say that it extends Prometheus capabilities and lets you store TSDB blocks in remote object storage such as Amazon’s S3. Thanos has a component called the bucket block viewer. Even though it ordinarily only works with data in remote object storage but we can make it work with local data as well by using the FILESYSTEM storage type.

To use Thanos to visualize our local TSDB, we have to pass an object storage configuration file. If your Prometheus stores data in, let’s say, /prometheus/data then the --objstore.config-file needs to point to a file that has the following content:

type: FILESYSTEM
config:
  directory: "/prometheus/data"

To start up the web interface for the storage bucket, execute the following command:

thanos tools bucket web --objstore.config-file=my-config-file.yaml 

The web UI becomes available on http://localhost:10902. Here is how it looks like on a very simple (where all of the blocks are contiguous and of the same compaction level) TSDB:

Thanos Bucket Viewer showing local Prometheus TSDB blocks

Feel free to use Thanos as a way to explore your Prometheus! The support for viewing vanilla Prometheus blocks has been merged on January 10th, 2021 so only new versions after this date will have this functionality. So, please mind this date and if this does not work for you then please update your Thanos.

Equivocal Puppet Class Parameters

While working with Puppet recently I have noticed that there is some funny business going on with the rules of parameter naming. The Puppet’s documentation states:

Parameter names begin with a dollar sign prefix ($). The parameter name after the prefix:

Must begin with a lowercase letter.

Can include lowercase letters.

Can include digits.

Can include underscores.

Let’s see if this is true. Tried applying this manifest with Puppet 5.5.10 which is available on Ubuntu Focal Fossa (20.04):

class test(
  String $_content
){
  file {'/tmp/helloworld':
    content =>  "${_content}\n",
  }
}


class { 'test':
  _content => "123"
}

And it does not work as expected:

$ puppet apply hello.pp
Error: Could not parse for environment production: Syntax error at '_content' (file: /home/gstatkevicius/dev/puppet_testing/manifest/hellofile.pp, line: 11, column: 3) on node gstatkevicius-desktop

Now, let’s try creating a simple hiera configuration:

---
:backends:
  - yaml
:yaml:
  :datadir: /home/gstatkevicius/dev/puppet_testing/hieradata
:hierarchy:
  - test
:logger: console
$ cat hieradata/test.yaml 
test::_a: "Hello World!"

It looks like the hiera part works:

hiera -c ./hiera.yaml -d test::_a
DEBUG: 2020-11-03 23:39:00 +0200: Hiera YAML backend starting
DEBUG: 2020-11-03 23:39:00 +0200: Looking up test::_a in YAML backend
DEBUG: 2020-11-03 23:39:00 +0200: Looking for data source test
DEBUG: 2020-11-03 23:39:00 +0200: Found test::_a in test
Hello World!

Now let’s see if applying the manifest with Puppet works:

$ puppet apply --hiera_config=hiera.yaml manifest/hellofile.pp
Notice: Applied catalog in 0.01 seconds
$ cat /tmp/helloworld
Hello World!

Oh, so now everything is OK? It seems that for APL – automatic parameter look-up – the rules are a bit different. My guess at this point would be that they are treated as regular variables instead. I personally haven’t found a way to instantiate a class where one parameter starts with an underscore. Thus, I think we can formulate one lesson:

To prevent your class from ever being instantiated by other classes in Puppet with explicit arguments, start at least one class parameter with an underscore.

But, one question remains – why is it actually considered a syntax error? What makes the underscore character forbidden in names of class parameters whereas it works for regular variables?

Now, I’m not an expert in how Puppet parsing but let’s take a short trip down Puppet’s code-base and see what’s happening.

A quick grep of Syntax error at shows that there is some kind of function SYNTAX_ERROR that gets used whenever there is a need to print a message that a syntax error has occurred.

Digging a bit deeper, it seems that there is some kind of parser being generated from a grammar. Other Puppet developers have kindly documented this process for us in docs/parser_work.md. We are finally able to find the grammar in lib/puppet/pops/parser/egrammar.ra.

Valid name of a variable seems to be expressed here in lexer2.rb:

PATTERN_DOLLAR_VAR     = %r{\$(::)?(\w+::)*\w+}

I believe that what is a valid argument passed to a class is defined here:

#---ATTRIBUTE OPERATIONS (Not an expression)
#
attribute_operations
  :                                                { result = [] }
  | attribute_operation                            { result = [val[0]] }
  | attribute_operations COMMA attribute_operation { result = val[0].push(val[2]) }

  # Produces String
  # QUESTION: Why is BOOLEAN valid as an attribute name?
  #
  attribute_name
    : NAME
    | keyword

A valid NAME is defined here in lib/puppet/pops/patterns.rb:

NAME = %r{\A((::)?[a-z]\w*)(::[a-z]\w*)*\z}

So, it seems like the argument’s name is rejected because it does not follow this regular expression even though it is accepted by the lexer. To be fair, Puppet’s documentation also states:

CAUTION: In some cases, names containing unsupported characters might still work. Such cases are bugs and could cease to work at any time. Removal of these bug cases is not limited to major releases.

All in all, it’s probably best to follow the letter of the law laid out in Puppet’s documentation as it says here but if you want to forbid the users of your class to pass arguments explicitly, start one of them with an underscore 🙃.