Compare commits
26 Commits
4d69d8d5ae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e3d320d581 | |||
| d560b83da9 | |||
| d670230112 | |||
| 8f2ad91e38 | |||
| cf467ff629 | |||
| e34aea7194 | |||
| a52a95c6bd | |||
| fc5afd8668 | |||
| d59033d4f6 | |||
| a3333adcb0 | |||
| 4801fe754e | |||
| 1b151fd27f | |||
| e33face849 | |||
| e4818e76e6 | |||
| bfe0f324fc | |||
| 4ab2c05be5 | |||
| c919d375a8 | |||
| 39f7d4e3fc | |||
| 4a9da2db37 | |||
| ac5f8bc1ca | |||
| 1fcb3dfdf8 | |||
| 9bb5afb01e | |||
| df2d2dd904 | |||
| 610d9553d4 | |||
| 8fa0febdf9 | |||
| 62fa060fdf |
33
.gitea/workflows/hugobuild.yaml
Normal file
33
.gitea/workflows/hugobuild.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
name: GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true # Fetch Hugo themes (true OR recursive)
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@v3
|
||||
with:
|
||||
hugo-version: '0.156.0'
|
||||
# extended: true
|
||||
|
||||
- name: Build
|
||||
run: hugo --minify --destination ./public
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: "docker.paulmontag.info/blog:${{ github.ref_name }}"
|
||||
|
||||
48
.github/workflows/hugo.yaml
vendored
48
.github/workflows/hugo.yaml
vendored
@@ -1,4 +1,3 @@
|
||||
# Sample workflow for building and deploying a Hugo site to GitHub Pages
|
||||
name: Deploy Hugo site to Pages
|
||||
|
||||
on:
|
||||
@@ -25,6 +24,7 @@ concurrency:
|
||||
# Default to bash
|
||||
defaults:
|
||||
run:
|
||||
# GitHub-hosted runners automatically enable `set -eo pipefail` for Bash shells.
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
@@ -32,14 +32,21 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HUGO_VERSION: 0.121.0
|
||||
DART_SASS_VERSION: 1.89.2
|
||||
HUGO_VERSION: 0.148.0
|
||||
HUGO_ENVIRONMENT: production
|
||||
TZ: America/Los_Angeles
|
||||
steps:
|
||||
- name: Install Hugo CLI
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
|
||||
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
|
||||
sudo dpkg -i ${{ runner.temp }}/hugo.deb
|
||||
- name: Install Dart Sass
|
||||
run: sudo snap install dart-sass
|
||||
run: |
|
||||
wget -O ${{ runner.temp }}/dart-sass.tar.gz https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
|
||||
tar -xf ${{ runner.temp }}/dart-sass.tar.gz --directory ${{ runner.temp }}
|
||||
mv ${{ runner.temp }}/dart-sass/ /usr/local/bin
|
||||
echo "/usr/local/bin/dart-sass" >> $GITHUB_PATH
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -47,21 +54,36 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Setup Pages
|
||||
id: pages
|
||||
uses: actions/configure-pages@v4
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Install Node.js dependencies
|
||||
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
|
||||
- name: Cache Restore
|
||||
id: cache-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
${{ runner.temp }}/hugo_cache
|
||||
key: hugo-${{ github.run_id }}
|
||||
restore-keys:
|
||||
hugo-
|
||||
- name: Configure Git
|
||||
run: git config core.quotepath false
|
||||
- name: Build with Hugo
|
||||
env:
|
||||
# For maximum backward compatibility with Hugo modules
|
||||
HUGO_ENVIRONMENT: production
|
||||
HUGO_ENV: production
|
||||
run: |
|
||||
hugo \
|
||||
--gc \
|
||||
--minify \
|
||||
--baseURL "${{ steps.pages.outputs.base_url }}/"
|
||||
--baseURL "${{ steps.pages.outputs.base_url }}/" \
|
||||
--cacheDir "${{ runner.temp }}/hugo_cache"
|
||||
- name: Cache Save
|
||||
id: cache-save
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
${{ runner.temp }}/hugo_cache
|
||||
key: ${{ steps.cache-restore.outputs.cache-primary-key }}
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v2
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: ./public
|
||||
|
||||
@@ -75,4 +97,4 @@ jobs:
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v3
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/public
|
||||
3
Dockerfile
Normal file
3
Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM nginx
|
||||
|
||||
COPY ./public /usr/share/nginx/html
|
||||
16
config.yml
16
config.yml
@@ -1,6 +1,7 @@
|
||||
baseURL: "https://d1ngd0.github.io/blog/"
|
||||
baseURL: "https://blog.paulmontag.info/"
|
||||
title: Paul Montag
|
||||
paginate: 5
|
||||
pagination:
|
||||
pagerSize: 5
|
||||
theme: PaperMod
|
||||
|
||||
enableRobotsTXT: true
|
||||
@@ -74,7 +75,7 @@ params:
|
||||
Title: "Hi there \U0001F44B"
|
||||
Content: Welcome to my blog
|
||||
|
||||
# socialIcons:
|
||||
socialIcons: []
|
||||
# - name: x
|
||||
# url: "https://x.com/"
|
||||
# - name: stackoverflow
|
||||
@@ -96,7 +97,7 @@ params:
|
||||
hiddenInSingle: true # hide on single page
|
||||
|
||||
editPost:
|
||||
URL: "https://github.com/d1ngd0/blog/content"
|
||||
URL: "https://git.paulmontag.info/paul-montag/blog/src/branch/main/content"
|
||||
Text: "Suggest Changes" # edit text
|
||||
appendFilePath: true # to append file path to Edit link
|
||||
|
||||
@@ -117,6 +118,10 @@ menu:
|
||||
name: about
|
||||
url: /about/
|
||||
weight: 10
|
||||
- identifier: resume
|
||||
name: resume
|
||||
url: /resume/
|
||||
weight: 20
|
||||
|
||||
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
|
||||
pygmentsUseClasses: true
|
||||
@@ -128,3 +133,6 @@ markup:
|
||||
# guessSyntax: true
|
||||
# lineNos: true
|
||||
# style: monokai
|
||||
caches:
|
||||
images:
|
||||
dir: :cacheDir/images
|
||||
|
||||
12
content/about.md
Normal file
12
content/about.md
Normal file
@@ -0,0 +1,12 @@
|
||||
+++
|
||||
title = 'About'
|
||||
date = 2024-10-07T06:53:00-05:00
|
||||
+++
|
||||
|
||||
I am a Father (one daughter), husband (one wife) and Software Engineer at a major retailer. I work on an observability team, providing the tools and infrastructure necessary for other engineers to understand the health of their applications. I also get up at crazy early or uncomfortably late times to dive into topics of interest, write blog posts and do a bit of light reading. Also, never totally mastered proper spelling, and lean quite heavily on spell check. If you see any mistakes, grammar or otherwise, let me know and I'll fix it; maybe.
|
||||
|
||||
During my day job I like to solve problems by automating them away. If something has to be done more than once I have a bash script in my `~/.bin/` directory, usually with some help text, to get it done quickly. If the problem is more complex I will create the tools needed (Often with Go, Rust or something near it) to solve things in the most simple yet generic way. I love taking on big novel challenges that require creativity or those annoying problems that no one could quite figure out.
|
||||
|
||||
In my role as a leader I try to create a light-hearted atmosphere when working with others, and have noticed this tends to foment excitement for the projects we are working on. I put effort into ensuring those that I work with find a comfortable space where creative problem solving can occur. I regularly adapt my processes (communication channels, ceremonies, project management styles) towards the group I am working with to ensure I create the best possible chance for a successful outcome. I have gotten the feedback on more than one occasion: "I really enjoyed this project because you made it fun", or "Your silliness made the project low stress, even when things were tough". I lead with humility and humor, and would rather ask a good question instead of giving command.
|
||||
|
||||
When I am not working I enjoy a plethora of circulating hobbies such as hiking, drumming, painting, drawing, photography, woodworking, cooking, barbecuing, running, writing and more software engineering. That isn't to say I am good at all these things, but I do enjoy them.
|
||||
35
content/posts/application_kustomize_image.md
Normal file
35
content/posts/application_kustomize_image.md
Normal file
@@ -0,0 +1,35 @@
|
||||
+++
|
||||
title = "Application Kustomize image"
|
||||
date = 2026-02-28T00:00:00-05:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
When creating [Applications](https://argo-cd.readthedocs.io/en/latest/user-guide/application-specification/) in [Argocd](https://argo-cd.readthedocs.io/en/stable/) I always prefer to use [kustomize](https://argo-cd.readthedocs.io/en/stable/user-guide/kustomize/) for my own manifests. It allows me to create a simple "base" of manifests and then alter it for the specific use case via the `kustomize` attribute within the application. This allows a single place (the `Application` or `ApplicationSet`) to stores all the high level information for configuring the installation.
|
||||
|
||||
While working on one project or another, I don't remember anymore why I needed to figure this out, I noticed the `images` attribute works differently in Argocd than it does in kustomize. **Since you the reader might be interested in the solution as opposed to the exploration, let me get that out of the way now**:
|
||||
|
||||
_format_: `- [old_image_name=]<image_name>:<image_tag>`
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
source:
|
||||
path: examples/helloWorld
|
||||
repoURL: 'https://github.com/kubernetes-sigs/kustomize'
|
||||
targetRevision: HEAD
|
||||
kustomize:
|
||||
images:
|
||||
- nginx=docker.io/nginx:v1.1.2
|
||||
```
|
||||
|
||||
Documentation for this syntax was hard to find, so I had to go digging through the [source](https://github.com/argoproj/argo-cd/blob/a3b4c8327f38ef2edfb3be6c68f96729ce98f9d8/pkg/apis/application/v1alpha1/types.go#L653-L654) to find out how it should be formatted.
|
||||
|
||||
Kustomize is less concise than argocd, having you [spell out each part](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/image.md). The same example above in kustomize would look like this.
|
||||
|
||||
```yaml
|
||||
images:
|
||||
- name: nginx
|
||||
newName: docker.io/nginx
|
||||
newTag: v1.1.2
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
+++
|
||||
title = 'Grabbing a Go Time with BPFTrace'
|
||||
date = 2024-10-06T08:45:05-06:00
|
||||
date = 2024-10-07T06:22:18-05:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
|
||||
93
content/posts/golang_package_structure.md
Normal file
93
content/posts/golang_package_structure.md
Normal file
@@ -0,0 +1,93 @@
|
||||
+++
|
||||
title = 'Golang Package Structure'
|
||||
date = 2025-11-10T00:00:00-05:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
I have been developing with Golang for about 10 years now, and over that time I have learned many things about the language, but the biggest one might be this.
|
||||
|
||||
**Directories are not just Directories**
|
||||
|
||||
Within golang a directory with new golang source code in it creates a new interface. One that requires you to think through what is public and what is private. One that forces you to consider the relationship between these packages since golang does not allow for [circular dependencies](https://groups.google.com/g/golang-nuts/c/8nwGtohyVtc/m/bFkZUV4X6gwJ). You need to strike a balance. I tend to see the following in codebases
|
||||
|
||||
1. Tiny Packages: Often separated by types (`routes/`, `config/`, `utils/`) that are so small almost everything in them is `Public`. This pattern leads to circular dependencies. (I once saw a `utils2/` to solve a circular dependency problem on `utils/`)
|
||||
2. One Massive package: Often `main` where everything is. Here the author will run into name collisions. Things in `main` also can't be pulled in as a library.
|
||||
|
||||
In both of these the biggest issue is not benefiting from what packages are supposed to do: Abstract away complexity! When a package is so tiny everything is `Public` it doesn't become the "Black Box" of power, solving some problem in a "elegant" way higher up. Additionally, throwing everything in main does the same thing. So what do you do.
|
||||
|
||||
## The Solution
|
||||
|
||||
I have heard the following quote, though I can't find it anymore. I am going to attribute it to someone who likely said it, I'm sure chatGPT will start attributing it to them thanks to me.
|
||||
|
||||
"Packages should define scope, not types" - Rob Pike ... probably.
|
||||
|
||||
In other words, your packages should be tools, not containers. Think of all the amazing libraries you use and love, they are often all in one big directory; that's because those directories are tools for developers to use to solve a generic problem. Here are some guidelines I set for myself when writing go.
|
||||
|
||||
### Put it in Main
|
||||
|
||||
Start by putting your logic in main. I have seen codebases bloat to 3x their size when all they are doing is serving a crappy HTTP API. Most likely you don't need some complex pattern, or crazy over abstracted interface setup to make code good. Don't let java fool you into thinking verbosity == professionalism. Readable code is maintainable code.
|
||||
|
||||
### Underscore types
|
||||
|
||||
I tend to organize my types with a simple nomenclature for my file names: `[interface]_[struct].go`. These are guidelines, not strict rules, there are always exceptions.
|
||||
|
||||
#### Each Interface with multiple implementations gets its own file.
|
||||
|
||||
The following interface I would put into a file called `runner.go`.
|
||||
|
||||
```go
|
||||
// Runner is an example interface to help you see what I mean, I am adding
|
||||
// a comment to make the point that you should always add comments to any interface
|
||||
// since you are creating the interface for future authors to create their own
|
||||
// types which implement it right? You aren't? Why are you creating an interface
|
||||
// then?
|
||||
type Runner interface {
|
||||
// run is a method which should also have a comment specifying what the caller
|
||||
// expects out of the method, Is it looking for any specific error types, what
|
||||
// does it do in certain situations. Spell it out for future you so they don't
|
||||
// have to go looking through code.
|
||||
func run(name string) error
|
||||
}
|
||||
|
||||
// RunnerFn is a helper type to create a runner from a function.
|
||||
// yeah any ancillary or helper functions for this type can go in
|
||||
// this file too
|
||||
type RunnerFn func(name string) error
|
||||
|
||||
func (rf RunnerFn) run(name string) {
|
||||
rf(name)
|
||||
}
|
||||
```
|
||||
|
||||
As you will notice, if you read the comments, we can put not just the interface, but other helper functions in this file too. This makes it easy to quickly find those functions which you know work with that interface type.
|
||||
|
||||
#### Structs built as different implementations of an interface get their own file
|
||||
|
||||
The following would be in `runner_helloworld.go`
|
||||
|
||||
```go
|
||||
import "fmt"
|
||||
|
||||
// HelloWorldRunner is a runner that outputs hello world and the name
|
||||
// supplied to the runner
|
||||
type HelloWorldRunner struct {}
|
||||
|
||||
func (hw *HelloWorldRunner) run(name string) {
|
||||
fmt.Println("hello world and " + name)
|
||||
}
|
||||
```
|
||||
|
||||
Now you have organization by type. Everything that is a `runner/` or `route/` etc gets "organized" by type, while not breaking the namespaces created through packages.
|
||||
|
||||
### Could it be a standalone repo?
|
||||
|
||||
I ask myself this question before creating a new package. Could I justify putting a `go.mod` file into the directory I am making, and push it to a new repository? would it be useful? If the answer is no I don't create a new directory. The point of packages is to abstract away complexity. If I can't envision the interface which is doing the abstracting, then it's to interconnected to justify putting it in it's own package.
|
||||
|
||||
## Conclusion
|
||||
|
||||
To be clear, I kinda hate that golang makes me do these things. Languages like rust, which allow multiple layers of public/private declaration solve these problems, though it is far more complex. Golang's mentality is simplicity, but that simplicity means we have to do things a bit different to solve problems with the language in a maintainable way. So:
|
||||
|
||||
- Stick with main until you see a clear problem which needs to be abstracted into a simple interface.
|
||||
- Aim for building tools with your packages, not containing types.
|
||||
- Utilize a nomenclature for file names to organize things instead of reaching for directories.
|
||||
- Justify every package abstraction layer you add by asking "could this be a standalone library?"
|
||||
10
content/posts/kla.md
Normal file
10
content/posts/kla.md
Normal file
@@ -0,0 +1,10 @@
|
||||
+++
|
||||
title = 'KLA'
|
||||
date = 2024-01-28T20:45:05-06:00
|
||||
draft = true
|
||||
+++
|
||||
|
||||
You know what I love, when things have documented HTTP Restful APIs. They make it possible for me to configure things without having to log in to some UI. It also enables me to automate or write scripts to make managing those things easier. Another thing I love is the terminal. I spend most of my engineering time in one, even preferring something like [helix](https://helix-editor.com/) over VSCode. Given these two loves it was only inevitable I ended up creating [kla](https://github.com/d1ngd0/kla).
|
||||
|
||||
Kla is an open source replacement for curl (you know, because we needed that) that also allows you to create templates, which can take arguments, to easily create https requests against a RESTful API. While I do regularly use it in my day to day now, it started as an excuse to learn rust.
|
||||
|
||||
66
content/resume.md
Normal file
66
content/resume.md
Normal file
@@ -0,0 +1,66 @@
|
||||
+++
|
||||
title = 'Resume'
|
||||
date = 2025-07-28T17:28:17-05:00
|
||||
draft = false
|
||||
+++
|
||||
|
||||
# Paul Montag - Software Engineer
|
||||
|
||||
Lead Software engineer who enjoys building strong teams specializing in golang development, Linux and Kubernetes administration, and Observability.
|
||||
|
||||
## Employment
|
||||
|
||||
### Target - Minneapolis, MN
|
||||
|
||||
#### Lead Engineer <sub>2019 - present</sub>
|
||||
|
||||
- Work with engineers throughout Target to better understand observability tooling
|
||||
- Manage replicated dataset with Petabytes of data, and billions of transactions daily
|
||||
- Manage highly resilient, zonal aware Clickhouse architecture in Kubernetes
|
||||
- Maintain Elasticsearch, Influx, Grafana architectures on VMware with ansible
|
||||
- Lead small team of engineers through various initiatives utilizing Agile methodology
|
||||
- Present talks within Target to create a culture of observability, and desired engineering practices
|
||||
- Create highly performant, specialized applications in golang to manage observability data
|
||||
|
||||
### ISeeMe - Eden Prairie, MN
|
||||
|
||||
#### Software Engineering Manager 2016 - 2019
|
||||
- Part of leadership team at ISeeMe to define year and quarterly rocks
|
||||
- Lead small team of engineers in completing rocks
|
||||
- Managed AWS billing
|
||||
- Lead Agile ceremonies
|
||||
- Maintained responsibilities from software engineer position
|
||||
|
||||
#### Software Engineer 2015 - 2016
|
||||
- Develop new features in PHP and Golang for personalized books
|
||||
- Maintain Linux (Ubuntu) servers for running personalization software
|
||||
- Maintain Magento web store
|
||||
- Maintain Kubernetes cluster utilizing AWS and bare metal (at printing locations)
|
||||
|
||||
### Meta 13 Interactive - St Cloud, MN
|
||||
|
||||
#### Web Programmer 2012 – 2015
|
||||
- Designed database structures using MSSQL and MySQL
|
||||
- Used existing frameworks to create applications for clients
|
||||
- Created custom frameworks using object oriented and procedural PHP
|
||||
- Used source control software such as Git and Mercurial
|
||||
- Communicated with clients to plan applications and discuss updates and regular maintenance
|
||||
- Created HTML and CSS using custom code as well as frameworks like Bootstrap
|
||||
- Worked both individually and with a team of developers to complete projects
|
||||
- Configured Apache, MySQL, and PHP on Linux servers as well as MSSQL and IIS on Windows servers
|
||||
|
||||
|
||||
## Skills
|
||||
|
||||
<sub>Languages: Golang, Rust, bash, javascript, HTML, CSS, YAML, JSON, python, awk</sub>
|
||||
|
||||
<sub>Tools: Ansible, git, github, Kubernetes, KubeBuilder, Argocd, Helm, kafka, elasticsearch, Clickhouse, influxdb, vim, helix, delve, chef, mercurial, Terraform, OpenTelemetry, Grafana, Postgres, MySQL/MariaDB, ChatGPT, Copilot, Burrow, BPFTrace, Linux, Ubuntu, CentOS, GCP, AWS</sub>
|
||||
|
||||
## Education
|
||||
|
||||
**Saint Cloud Technical College** _(Saint Cloud, MN 2008 – 2010)_ Associate of Applied Science Degree in Computer Programming
|
||||
|
||||
## Contact
|
||||
|
||||
- Blog: [https://blog.paulmontag.info/](https://blog.paulmontag.info/)
|
||||
- Github: [https://github.com/d1ngd0](https://github.com/d1ngd0)
|
||||
7
cspell.json
Normal file
7
cspell.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"userWords": [
|
||||
"Montag",
|
||||
"Clickhouse",
|
||||
"Magento"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 158 KiB |
Submodule themes/PaperMod updated: d6cd6d9175...5a4651783f
Reference in New Issue
Block a user