commit
d7698e7dd1
@ -0,0 +1,2 @@
|
||||
resources
|
||||
public
|
@ -0,0 +1,3 @@
|
||||
[submodule "themes/digitalgarden"]
|
||||
path = themes/digitalgarden
|
||||
url = https://github.com/apvarun/digital-garden-hugo-theme.git
|
@ -0,0 +1 @@
|
||||
hugo 0.93.3
|
@ -0,0 +1,13 @@
|
||||
FROM alpine as builder
|
||||
|
||||
COPY . /opt/
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
RUN apk add --no-cache hugo && \
|
||||
hugo --verbose --minify --enableGitInfo
|
||||
|
||||
FROM nginx
|
||||
COPY --from=builder /opt/public /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
@ -0,0 +1,73 @@
|
||||
baseURL = "https://vdhsn.com"
|
||||
languageCode = "en-us"
|
||||
title = "Adam Veldhousen - Raised Beds"
|
||||
theme = "digitalgarden"
|
||||
pygmentsStyle = "solarized-light" # solarized-light, -dark or -dark256
|
||||
summaryLength = 32
|
||||
enableRobotsTXT = true
|
||||
enableEmoji = true
|
||||
|
||||
googleAnalytics = "UA-PROPERTY_ID"
|
||||
|
||||
[params]
|
||||
favicon = "/party-tp.gif"
|
||||
|
||||
[markup]
|
||||
[markup.highlight]
|
||||
codeFences = true
|
||||
guessSyntax = false
|
||||
anchorlinenos = true
|
||||
lineNoStart = 1
|
||||
lineNos = true
|
||||
# lineNumbersInTable = true
|
||||
# noClasses = false
|
||||
style = "solarized-light"
|
||||
tabWidth = 2
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
name = 'Home'
|
||||
url = '/'
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
name = 'Health'
|
||||
url = '/health'
|
||||
weight = 2
|
||||
|
||||
[[menu.main]]
|
||||
name = 'Software'
|
||||
url = '/software'
|
||||
weight = 2
|
||||
|
||||
|
||||
[[menu.social]]
|
||||
name = 'Twitter'
|
||||
url = 'https://twitter.com/adamveld12'
|
||||
weight = 1
|
||||
|
||||
[[menu.social]]
|
||||
name = 'GitHub'
|
||||
url = 'https://github.com/adamveld12'
|
||||
weight = 2
|
||||
|
||||
[[menu.social]]
|
||||
name = 'LinkedIn'
|
||||
url = 'https://www.linkedin.com/in/aveldhousen/'
|
||||
weight = 3
|
||||
|
||||
[[menu.social]]
|
||||
name = "Keybase"
|
||||
url = "https://keybase.io/aveldhousen"
|
||||
weight = 3
|
||||
|
||||
[[menu.social]]
|
||||
name = 'Email'
|
||||
url = 'mailto:garden@vdhsn.com'
|
||||
weight = 8
|
||||
|
||||
[[menu.social]]
|
||||
name = 'RSS'
|
||||
url = '/index.xml'
|
||||
weight = 9
|
||||
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
title: Homepage
|
||||
---
|
||||
|
||||
# Hello 👋
|
||||
|
||||
**Welcome to my digital garden!**
|
||||
|
||||
I'm Adam V. and I write software for a living, and sometimes for fun.
|
||||
|
||||
[Resume](/resume_2019.pdf) | PGP: [A466CEE1415C0B9C](/gpg.pub)
|
||||
|
||||
|
||||
A digital garden is like a personal wiki and a knowledge database of thoughts and ideas. Similar to a traditional garden, a digital one will also container various kinds of content (plants), of which may even be unrelated to each other. Ideas are not refined, thoughts are not tailored. Here is an excellent write-up about the [history of digital gardens](https://maggieappleton.com/garden-history)
|
||||
|
||||
Twitter, for some, is also equivalent to a digital garden. It lets you share thoughts and ideas with everyone. But how often do you go back to those tweets? Not often. That's why you need a space for your ideas on the internet **that you own**. Check out `Digital gardens let you cultivate your own little bit of the internet` [post](https://www.technologyreview.com/2020/09/03/1007716/digital-gardens-let-you-cultivate-your-own-little-bit-of-the-internet/) by MIT technology review
|
||||
|
||||
<hr />
|
||||
|
||||
Building your own digital garden is not a fad. It's a necessity. Tools like Roam Research, Obsidian and Notion provided means to interlink content, even over a graphical way. Still not sold? Check out [The Digital Garden](https://dev.to/jbranchaud/the-digital-garden-l10) by Josh Branchaud.
|
||||
|
||||
Start collecting your ideas 💡, curate thought provoking & interesting content 💬 and learn.
|
||||
|
||||
→ [Software](/software)
|
||||
|
||||
→ [Health](/health)
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Health
|
||||
description: Health related notes. Physical, Mental, Spiritual, Social etc.
|
||||
---
|
||||
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: Software
|
||||
description: Software I use
|
||||
---
|
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "Set variables at build time in Golang"
|
||||
date: 2020-01-05T21:00:00Z
|
||||
tags: [golang, programming]
|
||||
draft: false
|
||||
---
|
||||
|
||||
For some CLI tools I use regularly I noticed that when I run the `version` flag get some version info that includes a
|
||||
git commit hash or a git tag to the command line:
|
||||
|
||||
```shell
|
||||
> kubectl version
|
||||
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"clean", BuildDate:"2019-11-13T11:23:11Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
|
||||
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
|
||||
```
|
||||
|
||||
I discovered a nice way to do this for go programs. You can specify a value for a global variable at build time using
|
||||
the `-ldflags "-X"` flag.
|
||||
|
||||
For this program:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
var Version = "dev"
|
||||
|
||||
func main(){
|
||||
fmt.Printf("I'm version <%s>", Version)
|
||||
}
|
||||
```
|
||||
You can use this one liner that builds your go program while setting the *Version* variable to the *sha* hash of the latest
|
||||
commit.
|
||||
|
||||
```shell
|
||||
> go build -ldflags "-X main.Version=$(git rev-parse HEAD)" -o hello_app main.go
|
||||
> hello_app
|
||||
I'm version ba83f7c418c669f705a3cce0c58a1f9129a3de14
|
||||
```
|
||||
|
||||
Note that you have to specify the full path of the package in your project, for example:
|
||||
|
||||
```bash
|
||||
# Your project looks like
|
||||
# - main.go <- module `github.com/myrepo/test`
|
||||
# - version <- `github.com/myrepo/test/version`
|
||||
# - sha.go <- has `var Version = "dev"`
|
||||
> go build -ldflags "-X github.com/myrepo/test/version.Version=$(git rev-parse HEAD)" -o hello_app main.go
|
||||
> hello_app
|
||||
I'm version ba83f7c418c669f705a3cce0c58a1f9129a3de14
|
||||
```
|
||||
|
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "Git tips - lint + test pre-commit hook"
|
||||
date: 2020-01-01T21:00:39Z
|
||||
tags: [git, bash]
|
||||
---
|
||||
|
||||
One of my favorite inventions is a `pre-commit` hook that auto runs test and lint commands from a `makefile` or
|
||||
`package.json` if they're found:
|
||||
|
||||
```bash {linenos=table}
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -f "$PWD/makefile" ] && [ ! -z "$(cat $PWD/makefile | grep '^lint:')" ]; then
|
||||
echo "running make lint"
|
||||
make lint
|
||||
elif [ -f "$PWD/package.json" ] && [ ! -z "$(cat $PWD/package.json | grep "^\"lint\":")" ]; then
|
||||
echo "running npm run lint"
|
||||
npm run lint
|
||||
fi
|
||||
|
||||
if [ -f "$PWD/makefile" ] && [ ! -z "$(cat $PWD/makefile | grep '^test:')" ]; then
|
||||
echo "running make test"
|
||||
make test
|
||||
elif [ -f "$PWD/package.json" ] && [ ! -z "$(cat $PWD/package.json | grep "^\"test\":")" ]; then
|
||||
echo "running npm run test"
|
||||
npm run test
|
||||
fi
|
||||
```
|
||||
|
||||
The `/usr/bin/env bash` piece ensures that the script has access to all of the environment variables you expect in your
|
||||
regular shell.
|
||||
|
||||
If the test or lint command fails then the `git commit` command fails. If I absolutely need to commit something in spite
|
||||
of the lint/test results I can do `git commit --no-verify` to skip the `pre-commit` hook.
|
||||
|
@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "Go Modules: How to replace a dependency with a local copy"
|
||||
date: 2020-03-20T20:35:25Z
|
||||
draft: false
|
||||
tags: [golang, modules]
|
||||
---
|
||||
|
||||
Lets say you're working on a go package and you need to replace a dependency with a specific version for testing. You can use the awesome `go mod edit -replace old=new[@version]` command to do this.
|
||||
|
||||
This command adds a `replace` directive to your `go.mod` that overrides any require statement versions for the matching module:
|
||||
|
||||
```bash {hl_lines=[13]}
|
||||
$ go mod edit -replace github.com/gobuffalo/packr=github.com/gobuffalo/packr@v2.8.0
|
||||
$ cat go.mod
|
||||
module github.com/adamveld12/riffraff
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/gobuffalo/packr v1.30.1
|
||||
github.com/golangci/golangci-lint v1.21.0 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
)
|
||||
|
||||
replace github.com/gobuffalo/packr => github.com/gobuffalo/packr v2.8.0
|
||||
```
|
||||
|
||||
Now when you build your go app, it will use `v2.8.0` of packr in place of the version specified in the `require` block.
|
||||
|
||||
But what if you want to use it similarly to `npm link`, where you want to replace a module with a local working copy?
|
||||
|
||||
Run the same command but omit the `@version` on the new package like so:
|
||||
|
||||
```bash {hl_lines=[15]}
|
||||
# clone your own copy and make some edits at ~/projects/packr
|
||||
$ cd ~/projects && git clone https://github.com/gobuffalo/packr
|
||||
$ cd ~/projects/riffraff && go mod edit -replace github.com/gobuffalo/packr=../packr
|
||||
$ cat go.mod
|
||||
module github.com/adamveld12/riffraff
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/gobuffalo/packr v1.30.1
|
||||
github.com/golangci/golangci-lint v1.21.0 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
)
|
||||
|
||||
replace github.com/gobuffalo/packr => ../packr #now points at your local copy
|
||||
```
|
||||
|
||||
|
||||
When you're all finished up you can remove the `replace` directive with the following command:
|
||||
|
||||
```bash
|
||||
$ go mod edit -dropreplace github.com/gobuffalo/packr
|
||||
```
|
@ -0,0 +1,120 @@
|
||||
---
|
||||
title: "Makefiles for Golang"
|
||||
date: 2020-01-07T00:50:32Z
|
||||
tags: [make, golang]
|
||||
draft: false
|
||||
---
|
||||
|
||||
[Make is a build automation tool from the late 70's][make-wiki] that's pretty popular in C and C++ world. Thanks to its age and
|
||||
popularity you can find tons of tutorials and Make is supported on basically every platform out there. I'm going to
|
||||
demonstrate how to set up a basic Makefile for Golang projects that will build, lint and test your code.
|
||||
|
||||
Make has a few simple rules that make it powerful, it expects that each task you create will be the name of an output file
|
||||
on disk. This is nice because if a file already exists with the same name as a task then Make will skip doing the work
|
||||
for that task.
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
For example if you create the following `Makefile` below and place it in the root of your project and run `make`, you will
|
||||
see a new `hello_world` binary built:
|
||||
|
||||
```makefile
|
||||
hello_world:
|
||||
go build -o hello_world main.go
|
||||
```
|
||||
|
||||
But at this point if you run it again, you'll see:
|
||||
|
||||
```sh
|
||||
make: 'hello_world' is up to date.
|
||||
```
|
||||
|
||||
So lets add a clean command to clean up the build output:
|
||||
|
||||
```makefile {linenos=table,hl_lines=["4-5"]}
|
||||
hello_world:
|
||||
go build -o hello_world main.go
|
||||
|
||||
clean:
|
||||
rm -rf ./hello_world
|
||||
```
|
||||
|
||||
One issue here is that the `clean` task will only work as long as there isn't a file in the project also named `clean`.
|
||||
If you want Make to ignore the file system for this task then you can add an entry to the `.PHONY` list:
|
||||
|
||||
```makefile {linenos=table,hl_lines=[7]}
|
||||
hello_world:
|
||||
go build -o hello_world main.go
|
||||
|
||||
clean:
|
||||
rm -rf ./hello_world
|
||||
|
||||
.PHONY: clean
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Next we can run tests. You can define variables in your makefile that run shell commands for their value. I'm running
|
||||
`go list` and filtering out the `vendor` folder so we can run tests for every package in our project. Remember to add
|
||||
that `test` task to the `.PHONY` list:
|
||||
|
||||
```makefile
|
||||
PKGS := $(shell go list ./... | grep -v vendor)
|
||||
|
||||
test:
|
||||
go test -v -cover $(PKGS)
|
||||
|
||||
.PHONY: test
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
Now that we can build and test our code, lets try to lint it. My lint tool of choice is [golangci-lint][golangcilint]
|
||||
so I like to add an install task that runs `go get` to install it. To do this I take advantage of a Make feature called
|
||||
prerequisite tasks, where you can list tasks that are required to execute before another task runs. This makes it easy
|
||||
to set up the install task as a dependency of our `lint` command, ensuring its installed every time we run it:
|
||||
|
||||
```makefile
|
||||
LINT_BIN := $(GOPATH)/bin/golangci-lint
|
||||
|
||||
$(LINT_BIN):
|
||||
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
|
||||
lint: $(LINT_BIN)
|
||||
@$(LINT_BIN) run -p format -p unused -p bugs # The '@' symbol hides the command in the output
|
||||
|
||||
.PHONY: lint
|
||||
```
|
||||
|
||||
## Putting it together
|
||||
|
||||
Below is the complete makefile. I added the `.SHELLFLAGS` variable, which sets various flags in the shell that Make executes
|
||||
your commands in. The `-euo pipefail` runs your commands in a type of [strict mode in Bash][strict-mode] which will catch
|
||||
errors as they happen and make your life debugging shells scripts generally much easier.
|
||||
|
||||
```makefile
|
||||
.SHELLFLAGS := -euo pipefail
|
||||
PKGS := $(shell go list ./... | grep -v vendor)
|
||||
LINT_BIN := $(GOPATH)/bin/golangci-lint
|
||||
|
||||
hello_world:
|
||||
go build -o hello_world main.go
|
||||
|
||||
lint: $(LINT_BIN)
|
||||
@$(LINT_BIN) run -p format -p unused -p bugs # The '@' symbol hides the command in the output
|
||||
|
||||
test:
|
||||
go test -v -cover $(PKGS)
|
||||
|
||||
$(LINT_BIN):
|
||||
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
|
||||
clean:
|
||||
rm -rf ./hello_world
|
||||
|
||||
.PHONY: clean lint test
|
||||
```
|
||||
|
||||
[make-wiki]: https://en.wikipedia.org/wiki/Make_(software)
|
||||
[strict-mode]: http://redsymbol.net/articles/unofficial-bash-strict-mode/
|
@ -0,0 +1,92 @@
|
||||
---
|
||||
title: "Kubernetes load balancing with Metal LB"
|
||||
date: 2020-01-02T20:57:36Z
|
||||
tags: ["kubernetes", "homelab", "networking"]
|
||||
draft: false
|
||||
---
|
||||
|
||||
If you run Kubernetes on an IaaS provider like AWS or GCE and create a service with the *LoadBalancer* type, there is glue code included in kubernetes itself that will provision an ELB/ALB for you automatically. When you're running k8s on prem or at home any service you create with the *LoadBalancer* service type will hang indefinitely since there is no way to provision external IPs on your router out of the box. This is where Metal LB comes in.
|
||||
|
||||
[Metal LB][metallb] is a project that implements load balancing for on premises based Kubernetes clusters by responding to ARP requests directly on your network with the MAC address of the worker nodes. This means no setup is required in most cases and you get a nice internal IP that you can port forward on your router. In this post I will walk you through high level set up so you can get traffic from the internet hitting your service in a scalable way.
|
||||
|
||||
## Setup Metal LB
|
||||
|
||||
Installation is easy but you have to make sure you're using a compatible networking add on. I would recommend [Flannel][flannel] or [Kube Router][kuberouter] but there are many others supported with caveats that you can [look in their compatibility table][metallbcompattable].
|
||||
|
||||
Next you can install Metal LB on your cluster like so:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.3/manifests/metallb.yaml
|
||||
```
|
||||
|
||||
Then set up a config map with an IP address pool. This IP address pool should be in the subnet that is set up on your router or traffic will be dropped. This means that if your router is set up to give out IPs in the range of `192.168.0.2-192.168.0.254` then you should make sure the pool is in that range.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
namespace: metallb-system
|
||||
name: config
|
||||
data:
|
||||
config: |
|
||||
address-pools:
|
||||
- name: default
|
||||
protocol: layer2
|
||||
addresses:
|
||||
- 192.168.0.240-192.168.0.250
|
||||
```
|
||||
|
||||
Now lets run a pod and service to see this in action. Apply the following with *kubectl*:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
name: web
|
||||
port: 80
|
||||
selector:
|
||||
app: whoami
|
||||
type: LoadBalancer
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
namespace: default
|
||||
name: whoami
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: containous/whoami
|
||||
ports:
|
||||
- name: web
|
||||
containerPort: 80
|
||||
```
|
||||
|
||||
Finally get the external IP address by doing `kubectl get svc whoami`. Visit that IP on port 80 and you should see some output.
|
||||
|
||||
And that's all there is to it. From here you should be able to port forward 80 to that IP and access the service from the internet with the IP given to you by your ISP.
|
||||
|
||||
Next I will show how to set up [Traefik][traefik], a popular and powerful loadbalancer. We'll be able to port forward to traefik and route to multiple services in any way we want.
|
||||
|
||||
|
||||
[metallb]: https://metallb.universe.tf/
|
||||
[metallbcompattable]: https://metallb.universe.tf/installation/network-addons/
|
||||
[kuberouter]: https://www.kube-router.io/
|
||||
[flannel]: https://github.com/coreos/flannel/blob/master/Documentation/kubernetes.md
|
||||
[cillium]: https://github.com/cilium/cilium
|
||||
[traefik]: https://docs.traefik.io/v2.0/
|
@ -0,0 +1,76 @@
|
||||
---
|
||||
title: "How to setup a local persistent volume in kubernetes"
|
||||
date: 2020-01-04T23:14:35Z
|
||||
draft: false
|
||||
tags: [kubernetes, configuration, storage]
|
||||
---
|
||||
|
||||
|
||||
I'm running a single node kubernetes cluster and one of the first things I needed was persistent storage. To create a volume that you can mount into your containers in a pod you have to create a *PersistentVolume (PV)* and then request it with a *PersistentVolumeClaim (PVC)*.
|
||||
|
||||
|
||||
Create a *PersistentVolume (PV)* object, pointing at a path on your host. Note the `spec.capacity.storage`, `spec.hostPath.path` and change these accordingly.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: persistent-test-volume
|
||||
labels:
|
||||
name: persistent-test-volume
|
||||
spec:
|
||||
volumeMode: Filesystem
|
||||
storageClassName: standard
|
||||
accessModes:
|
||||
- ReadWriteOnce # type of access
|
||||
capacity:
|
||||
storage: 100Gi # Size of the volume
|
||||
hostPath:
|
||||
path: "/storage/volumes/test-volume"
|
||||
```
|
||||
|
||||
Next you must create a *PersistentVolumeClaim (PVC)* to request access to the resources of the *PersistentVolume (PV)*.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: persistent-test-volume-claim
|
||||
spec:
|
||||
volumeMode: Filesystem
|
||||
storageClassName: standard
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
selector:
|
||||
matchLabels:
|
||||
name: persistent-test-volume
|
||||
```
|
||||
|
||||
Now that we've set these two resources up, we can create a pod with a container that references the *PVC* we made above in the `spec.volumes`
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pv-tester
|
||||
namespace: default
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: pv-tester
|
||||
image: busybox
|
||||
command: ["/bin/sh", "-c", "echo 'Hello volume' > /test_vol/hello.txt"]
|
||||
volumeMounts:
|
||||
- name: vol
|
||||
mountPath: /test_vol
|
||||
volumes:
|
||||
- name: vol
|
||||
persistentVolumeClaim:
|
||||
claimName: persistent-test-volume-claim
|
||||
```
|
||||
|
||||
You now should be able to see the `hello.txt` file at the path `/storage/volumes/` on the host machine.
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "Reading List"
|
||||
date: 2022-03-09
|
||||
draft: false
|
||||
description: Cookie sweet donut candy pastry apple dolor orange lollipop biscuit. Muffin cream ipsum ipsum sprinkles sugar tiramisu pastry sweet tiramisu.
|
||||
---
|
||||
|
||||
## Kubernetes
|
||||
|
||||
- [Kubernetes in Production: The Ultimate Guide to Monitoring Resource Metrics with Prometheus](https://www.replex.io/blog/kubernetes-in-production-the-ultimate-guide-to-monitoring-resource-metrics)
|
||||
- [K8s Monitor Pod CPU and memory usage with Prometheus](https://itnext.io/k8s-monitor-pod-cpu-and-memory-usage-with-prometheus-28eec6d84729)
|
||||
- [Understanding machine cpu usage](https://www.robustperception.io/understanding-machine-cpu-usage)
|
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Using credentials in your Makefiles"
|
||||
date: 2020-01-14T05:57:04Z
|
||||
draft: false
|
||||
tags: [make, programming]
|
||||
---
|
||||
|
||||
Recently while working on a project I needed a way to include secrets in my repo for local development that integrated
|
||||
with my *Make* based build setup. I found that a nice way to do this is with the `include` command in *Make*.
|
||||
|
||||
The `include` command allows you to include external files to augment your makefile. First I created a `secrets.mk` and
|
||||
declared variables in it for my API keys.
|
||||
|
||||
```makefile {linenos=table}
|
||||
# secrets.mk
|
||||
github_access_token := 'xxxxxxxxxxxxxxxxx'
|
||||
slack_access_token := 'xxxxxxxxxxxxxxxxx'
|
||||
```
|
||||
|
||||
Then in my makefile I added `include secrets.mk`. Don't forget to add `secrets.mk` to your `.gitignore`!
|
||||
|
||||
```makefile
|
||||
include secrets.mk
|
||||
.PHONY: dev
|
||||
|
||||
dev: app
|
||||
GITHUB_ACCESS_TOKEN=$(github_access_token) \
|
||||
SLACK_ACCESS_TOKEN=$(slack_access_token) \
|
||||
./app
|
||||
|
||||
app:
|
||||
go build -o app main.go
|
||||
```
|
||||
|
||||
One issue with this I ran into was in my CI build I didn't have a `secrets.mk` file. *Make* will fail if it cannot find an
|
||||
include file on disk or if it *Make* unable to find a rule to generate one. Luckily you can preprend a dash to the include
|
||||
statement to make it optional, so the rest of your tasks that don't require an include will still be usable.
|
||||
|
||||
```makefile
|
||||
-include secrets.mk
|
||||
```
|
||||
|
||||
Lastly, another neat thing about using this technique is that if an include isn't found, *Make* will look for a task that
|
||||
can generate it. I added a `secrets.mk` task to my makefile that creates a stub include file that can get filled out with
|
||||
the correct credentials if needed.
|
||||
|
||||
```makefile
|
||||
include secrets.mk
|
||||
.PHONY: dev
|
||||
|
||||
dev: app
|
||||
GITHUB_ACCESS_TOKEN=$(github_access_token) \
|
||||
SLACK_ACCESS_TOKEN=$(slack_access_token) \
|
||||
./app
|
||||
|
||||
app:
|
||||
go build -o app main.go
|
||||
|
||||
secrets.mk:
|
||||
echo "github_access_token:='xxxxxxxx'" > ./secrets.mk
|
||||
echo "slack_access_token:='xxxxxxxxx'" >> ./secrets.mk
|
||||
```
|
@ -0,0 +1 @@
|
||||
Nothing here yet.
|
@ -0,0 +1,35 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $description := .Description }}
|
||||
|
||||
<div class="flex h-screen relative">
|
||||
<section
|
||||
class="w-full h-full md:min-w-[400px] md:w-1/4 bg-slate-50 dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex flex-col py-3 overflow-y-auto scroll-area">
|
||||
<a href="{{ .Permalink }}">
|
||||
<h2 class="font-bold mb-5 py-1 pl-12 pr-3 md:px-3">{{ .Title }}</h2>
|
||||
</a>
|
||||
<div class="space-y-2.5">
|
||||
{{ range .Data.Pages -}}
|
||||
<a class="block px-3 py-4 hover:bg-slate-200 dark:hover:bg-slate-700" href="{{ .RelPermalink }}">
|
||||
|
||||
{{ $title := .Params.title }}
|
||||
{{ with .Params.images }}
|
||||
{{- range first 1 . }}
|
||||
<img class="rounded max-w-full mb-4" src="{{ . }}" alt="{{ $title }}" />
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
<h3 class="text-lg font-semibold mb-0.5">{{ $title }}</h3>
|
||||
<div class="text-sm text-slate-500 dark:text-slate-400 line-clamp-2">
|
||||
{{ $description }}
|
||||
</div>
|
||||
</a>
|
||||
{{ end -}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main class="hidden md:grid h-screen place-items-center flex-1">
|
||||
<p class="text-center p-8 text-2xl text-slate-300 dark:text-slate-700">Select a post to read</p>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
@ -0,0 +1,58 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $permalink := .RelPermalink }}
|
||||
|
||||
<div class="flex h-screen relative">
|
||||
|
||||
<section
|
||||
class="will-change-transform transform transition-transform -translate-x-[200%] absolute top-0 left-0 lg:relative
|
||||
lg:translate-x-0 lg:min-w-[400px] lg:w-1/4 h-full bg-slate-50 dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 lg:flex flex-col py-3 overflow-y-auto scroll-area">
|
||||
{{$Section := .Site.GetPage "section" .Section }}
|
||||
{{ with $Section }}
|
||||
<a href="{{ .Permalink }}">
|
||||
<h2 class="font-bold mb-5 py-1 pl-12 pr-3 md:px-3">{{ .Title }}</h2>
|
||||
</a>
|
||||
<div class="space-y-2.5">
|
||||
{{ range .Data.Pages -}}
|
||||
{{ $isCurrentPage := eq .RelPermalink $permalink }}
|
||||
<a class="block px-3 py-4 {{ if $isCurrentPage }} bg-slate-900 dark:bg-slate-700 text-slate-50 {{ else }} hover:bg-slate-200 dark:hover:bg-slate-700 {{ end }}"
|
||||
href="{{ .RelPermalink }}">
|
||||
{{ $title := .Params.title }}
|
||||
{{ with .Params.images }}
|
||||
{{- range first 1 . }}
|
||||
<img class="rounded max-w-full mb-4" src="{{ . }}" alt="{{ $title }}" />
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
<h3 class="text-lg font-semibold mb-0.5">{{ $title }}</h3>
|
||||
<div
|
||||
class="text-sm {{ if $isCurrentPage }} text-slate-400 {{ else }} text-slate-500 dark:text-slate-400 {{ end }} line-clamp-2">
|
||||
{{ .Summary }}
|
||||
</div>
|
||||
</a>
|
||||
{{ end -}}
|
||||
</div>
|
||||
{{ end -}}
|
||||
</section>
|
||||
|
||||
<div class="overflow-y-auto h-screen w-full">
|
||||
<article class="px-6 py-20 w-full mx-auto prose lg:prose-lg h-fit dark:prose-invert prose-img:mx-auto">
|
||||
|
||||
{{ $title := .Params.title }}
|
||||
<h1 class="!mb-2">{{ $title }}</h1>
|
||||
{{ if .Date }}
|
||||
<p class="text-sm text-slate-500 !mb-8">{{ .Date.Format "January 2, 2006" }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Params.images }}
|
||||
{{- range first 1 . }}
|
||||
<img class="rounded max-w-full mx-auto mb-4" src="{{ . }}" alt="{{ $title }}" />
|
||||
{{ end -}}
|
||||
{{ end }}
|
||||
|
||||
{{ .Content }}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{ end }}
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,83 @@
|
||||
<aside
|
||||
class="will-change-transform transform transition-transform -translate-x-full absolute top-0 left-0 md:relative md:translate-x-0 w-3/4 md:w-60 h-full min-h-screen p-3 bg-slate-50 dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex flex-col gap-2.5 z-20 sidebar">
|
||||
<p class="font-bold mb-5 flex items-center gap-2">
|
||||
<button
|
||||
aria-label="Close sidebar"
|
||||
class="md:hidden menu-trigger-close p-1 rounded text-slate-800 dark:text-slate-50 hover:bg-slate-200 dark:hover:bg-slate-700">
|
||||
{{- partial "icon/closeIcon.html" . -}}
|
||||
</button>
|
||||
<a href="{{ .Site.BaseURL }}" class="px-2">
|
||||
<span>{{ .Site.Title }}</span>
|
||||
</a>
|
||||
<button
|
||||
aria-label="Toggle dark mode"
|
||||
class="dark-mode-toggle p-2 rounded border dark:border-slate-700 hover:bg-slate-200 dark:hover:bg-slate-700">
|
||||
{{- partial "icon/sunIcon.html" . -}}
|
||||
</button>
|
||||
</p>
|
||||
|
||||
{{ $permalink := cond (eq (len .RelPermalink) 1) .RelPermalink (strings.TrimRight "/" .RelPermalink) }}
|
||||
<ul class="list-none flex flex-col gap-1">
|
||||
{{ range .Site.Menus.main }}
|
||||
<li>
|
||||
<a class="px-2 py-1.5 rounded-md text-sm flex items-center justify-between {{ if eq .URL $permalink }} bg-slate-800 text-white dark:bg-slate-50 dark:text-slate-800 dark:selection:bg-slate-400 {{ else }} hover:bg-slate-200 dark:hover:bg-slate-700 {{ end }}"
|
||||
href="{{ .URL }}" {{ if strings.HasPrefix .URL "http" }} target="_blank" rel="noopener" {{ end }}>
|
||||
<span>{{ .Name }}</span>
|
||||
{{ if strings.HasPrefix .URL "http" }}
|
||||
<span>
|
||||
{{- partial "icon/externalIcon.html" . -}}
|
||||
</span>
|
||||
{{ end }}
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
<div class="flex-1"></div>
|
||||
|
||||
{{ if .Site.Params.newsletter }}
|
||||
{{- partial "newsletter-widget.html" . -}}
|
||||
{{ end }}
|
||||
|
||||
<ul class="list-none flex flex-wrap justify-center gap-1 pt-2 border-t border-slate-200 dark:border-slate-600">
|
||||
{{ range .Site.Menus.social }}
|
||||
<li>
|
||||
<a class="px-2 py-1.5 rounded-md text-sm block text-slate-800 dark:text-slate-50 {{ if eq (.URL|absURL) $permalink }} bg-slate-800 text-white dark: {{ else }} hover:bg-slate-200 dark:hover:bg-slate-700 {{ end }}"
|
||||
href="{{ .URL }}" target="_blank" rel="noopener noreferrer">
|
||||
<span class="sr-only">{{ .Name }}</span>
|
||||
{{ if eq .Name "GitHub" }}
|
||||
<span>{{- partial "icon/githubIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Keybase" }}
|
||||
<span>{{- partial "icon/keybaseIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Twitter" }}
|
||||
<span>{{- partial "icon/twitterIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "LinkedIn" }}
|
||||
<span>{{- partial "icon/linkedinIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Instagram" }}
|
||||
<span>{{- partial "icon/instagramIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Dribbble" }}
|
||||
<span>{{- partial "icon/dribbbleIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Codepen" }}
|
||||
<span>{{- partial "icon/codepenIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Twitch" }}
|
||||
<span>{{- partial "icon/twitchIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "Email" }}
|
||||
<span>{{- partial "icon/emailIcon.html" . -}}</span>
|
||||
{{ else if eq .Name "RSS" }}
|
||||
<span>{{- partial "icon/rssfeedIcon.html" . -}}</span>
|
||||
{{ end }}
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div
|
||||
class="fixed bg-slate-700 bg-opacity-5 transition duration-200 ease-in-out inset-0 z-10 pointer-events-auto md:hidden left-0 top-0 w-full h-full hidden menu-overlay">
|
||||
</div>
|
||||
|
||||
<button
|
||||
aria-label="Toggle Sidebar"
|
||||
class="md:hidden absolute top-3 left-3 z-10 menu-trigger p-1 rounded text-slate-800 dark:text-slate-50 hover:bg-slate-100">
|
||||
{{- partial "icon/menuIcon.html" . -}}
|
||||
</button>
|
@ -0,0 +1,23 @@
|
||||
SHA=$(shell git rev-parse --short=6 HEAD)
|
||||
BUILD_DATE=$(shell date --iso-8601=minutes)
|
||||
|
||||
.PHONY: build docker install dev
|
||||
|
||||
dev:
|
||||
hugo serve -D --enableGitInfo
|
||||
|
||||
docker:
|
||||
docker build \
|
||||
--label="org.opencontainers.image.created=${BUILD_DATE}" \
|
||||
--label="org.opencontainers.image.source=https://git.vdhsn.com/adam/garden.git" \
|
||||
--label="org.opencontainers.image.url=https://git.vdhsn.com/adam/garden" \
|
||||
--label="org.opencontainers.image.revision=${SHA}" \
|
||||
--label="org.opencontainers.image.licenses=MIT" \
|
||||
--label="org.opencontainers.image.authors=Adam Veldhousen <adam@vdhsn.com>" \
|
||||
-t vdhsn/garden:latest .
|
||||
|
||||
build:
|
||||
hugo --verbose --minify --enableGitInfo
|
||||
|
||||
install:
|
||||
asdf local
|
@ -0,0 +1,85 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
=YqU0
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
@ -0,0 +1 @@
|
||||
Subproject commit b8339def522c34047459c8a25938c02f8ccbfbb5
|
Loading…
Reference in new issue