Compare commits

...

10 Commits

10 changed files with 534 additions and 131 deletions

View File

@@ -41,7 +41,7 @@ $(databases): .dbs/%.rec: %/ | .dbs/
# This two-variable read can only happen because of the quotes in the titles.
db.rec: $(databases)
$(warning rebuilding from $? )
$(info rebuilding from $? )
printf '%s\n' '%rec: guide' > $@
printf '%s\n' '%key: title' >> $@
printf '%s\n' '%type: requires rec guide' >> $@

View File

@@ -1,129 +0,0 @@
---
title: "cron"
tags: [ "basics", "time" ]
---
# Cronie
The `cronie` program is also known as `crond`.
## Install
```sh
sudo apt search -n ^cron
```
Once installed, search for the service name, and start it.
```sh
sudo systemctl list-unit-files | grep cron
sudo systemctl enable --now $NAME
```
## Usage
Show your current crontab:
```sh
crontab -l
```
You can put this in a file and edit it:
```sh
crontab -l > $filename
echo '39 3 */3 * * /bin/tar czf /tmp/etc_backup.tgz /etc/' >> $filename
```
Then apply that crontab:
```sh
crontab $filename
rm $filename
```
The `cron` program will check your syntax before adding the tab.
Your crontab file sits somewhere in `/var/spool/`.
Probably in `/var/spool/cron`.
## Syntax
`* * * * *`
These five points refer to:
`minute hour day month weekday`
So '3pm every Sunday' would be:
`0 15 * * 7`
Here 'Sunday' is indicated by "7", and '3pm' is 'the 15th hour'.
The minute is '0' (i.e. '0 minutes past three pm').
Doing the same thing, but only in February, would be:
`0 15 * 2 7`
### Variables
`cronie` doesn't know where you live, so to put something in your `$HOME` directory, you have to tell it:
```sh
echo "HOME=$HOME" > $filename
crontab -l >> $filename
crontab $filename
```
`cronie` doesn't know where anything lives, including programs.
You can give it your usual `$PATH` variable like this:
```sh
echo $PATH > $filename
crontab -l >> $filename
crontab $filename
```
Now instead of doing this
`40 */3 * * * /usr/bin/du -sh $HOME/* | sort -h > $HOME/sum.txt`
You can simply do this:
`40 */3 * * * du -sh $HOME/* | sort -h > $HOME/sum.txt`
## Run as Root
You can execute a script as root by putting it into a directory, instead of in the tab.
Look at the available cron directories:
```sh
ls -d /etc/cron.*
```
Make a script which runs daily:
```sh
f=apt_update.sh
echo '#!/bin/bash' > $f
echo 'apt update --yes' >> $f
chmod +x $f
sudo mv $f /etc/cron.daily/
```
### Testing with runparts
Run-parts runs all executable scripts in a directory.
```sh
run-parts /etc/cron.hourly
```
# Troubleshooting
### `date` Commands
Cron doesn't understand the `%` sign, so if you want to use `date +%R`, then it should be escaped with a backslash: `date +\%R`.

View File

@@ -100,7 +100,13 @@ Try:
- `<Esc>kcw`
- ls -a<Esc>xxxx
Works with `python` too:
Readline can work with python one you set `PYTHON_BASIC_REPL` to `true`.
```sh
echo 'export PYTHON_BASIC_REPL=true' >> ~/.bashrc
exec bash
```
```python
im<C-n>os<Return>

54
data/newsraft.md Normal file
View File

@@ -0,0 +1,54 @@
---
title: "Newsraft"
tags: [ "data", "RSS" ]
requires: [ "Shell Scripts" ]
---
# Setup
Install newsraft, then:
```sh
mkdir ~/.config/newsraft
echo 'https://codeberg.org/newsraft/newsraft.atom "Newsraft git"' >> ~/.config/newsraft/feeds
newsraft
```
# Commands
Copy the default config file:
```
cp /usr/share/doc/newsraft/example/config ~/.config/newsraft/config
```
Add a line to check the man page while inside the program:
```
bind M exec man newsraft
```
This will fail, because the letter 'M' is taken by `mpv`.
Add this line to take the default link, and place it in a list of videos.
```
bind V mark-read; exec echo "%l" >> ~/.cache/vidlist.txt
```
# Videos
You can get an RSS feed from any YouTube video with this script:
```
#!/bin/sh
CHANNEL_ID="$(curl -s "$1" | tr ',' '\n' | grep -Po 'channelId":"\K[\w+-]+' | tail -1)"
FEED_URL="https://www.youtube.com/feeds/videos.xml?channel_id=$CHANNEL_ID"
CHANNEL_NAME="$(curl -s "$FEED_URL" | grep -m 1 -Po 'title\>\K[\w\s]+')"
printf '%s "%s"\n' "$FEED_URL" "$CHANNEL_NAME"
```

View File

@@ -0,0 +1,75 @@
---
title: "Store Host Password"
tags: [ "system", "ansible" ]
---
Make a hosts file with one host (your computer) and one variable, just to test:
```sh
hosts_file=hosts
fort="$(fortune -s | head -1)"
cowvar=cowsays
echo "[cows]
$HOSTNAME $cowvar='${fort}'" > "${hosts_file}"
```
Now ansible should be able to show that '${cowvar}' in a debug message:
```sh
ansible -i "$hosts_file" -m debug -a "msg='{{ ${cowvar} }}'" $HOSTNAME
```
Now to convert the hosts file to yaml, because it's very fashionable:
```sh
yaml_hosts=hosts.yaml
ansible-inventory -i ${hosts_file} --list -y | tee "${yaml_hosts}"
```
Now you should see where the `cowsays` variable goes.
You can safely place your `sudo` password next to that variable goes with `ansible-vault`, which will encrypt just that string.
```sh
pass="your password"
ansible-vault encrypt_string --name='ansible_sudo_pass' "${pass}"
```
If that works, you can add the password, but in `yaml` format.
You can do this manually, or use `gawk` to add ten spaces in front of the lines:
```sh
pass="your password"
ansible-vault encrypt_string --name='ansible_sudo_pass' "${pass}" | awk '{print " " $0}' >> "${yaml_hosts}"
```
Now to check that the inventory file works okay:
```sh
ansible-inventory -i ${yaml_hosts} --list -y
ansible -i "$hosts_file" -m debug -a "msg='{{ ${cowvar} }}'" $HOSTNAME
```
If that works, you can echo the debug message while becoming root.
Just add the `-J` flag so it will ask for the password:
```sh
ansible -i "${yaml_hosts}" -m debug -a "msg='{{ ${cowvar} }}'" $HOSTNAME --become -J
ansible -i "${yaml_hosts}" -m debug -a "msg={{ ansible_sudo_pass }}" $HOSTNAME --become -J
```
Now you can update using Ansible.
For Arch Linux:
```sh
ansible -i "${yaml_hosts}" -m community.general.pacman -a 'upgrade=true update_cache=true' $HOSTNAME --become -J
```
For Debian:
```sh
ansible -i "${yaml_hosts}" -m ansible.builtin.apt -a 'upgrade=full' $HOSTNAME --become -J
```

View File

@@ -0,0 +1,20 @@
---
title: "Clean Your Downloads"
tags: [ "system", "tmpfs" ]
---
'Downloads` directory always too full of crap?
Make it a temporary filesystem!
Everything will be deleted whenever you reboot.
```sh
rm -rf ~/Downloads # Be brave!
mkdir Downloads
cp /etc/fstab /tmp/
echo "tmpfs $HOME/Downloads tmpfs defaults,size=1G 0 0" | sudo tee -a /etc/fstab
sudo systemctl daemon-reload # Ignore this if you don't use systemd
sudo mount -a
mount | tail -1
```

View File

@@ -0,0 +1,206 @@
---
title: "Kubernetes Basics"
tags: [ "virtualization", "kubernetes" ]
requires: [ "Kubernetes Setup" ]
---
Install `kubectl`.
> **NB:** Debian requires manual installation.[^kubedeb]
[^kubedeb]: https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
# Read
1. Check the namespace: `kubectl get namespaces`
1. Check the `${kube-system}` namespace: `kubectl get deployments.apps --namespace kube-system'
1. Check host computers: `kubectl get nodes`
1. Check pods: `kubectl get pods`
```tree
Namespaces
├── Node_1: minikube
│ ├── deployment_1: nginx
│ │   ├── pod_1
│ │   └── pod_2
│ ├── deployment_2: database
│ │   ├── pod_1
│ │   ├── pod_2
│ │   ├── pod_3
│ │   └── pod_4
│   ├── deployment_3: prometheus
│   └── deployment_4: idk probably yaml
└── Node_1: physical server
├── deployment_1: nginx
│   ├── pod_1
│   └── pod_2
├── deployment_2: database
│   ├── pod_1
│   ├── pod_2
│   ├── pod_3
│   └── pod_4
├── deployment_3: prometheus
└── deployment_4: Abandoned wiki
```
## More Information
```bash
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-depl-68c944fcbc-2xbvq 1/1 Running 0 20m
$ kubectl describe pod nginx-depl-68c944fcbc-2xbvq
Name: nginx-depl-68c944fcbc-2xbvq
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.59.107
Start Time: Fri, 29 Aug 2025 19:26:29 +0200
Labels: app=nginx-depl
pod-template-hash=68c944fcbc
Annotations: <none>
Status: Running
IP: 10.244.0.3
IPs:
IP: 10.244.0.3
Controlled By: ReplicaSet/nginx-depl-68c944fcbc
Containers:
nginx:
Container ID: docker://aaa68e90ed9237dc0f98f9a21b0d7ddf3113188c62e72242d30cab4a43cbff98
Image: nginx
Image ID: docker-pullable://nginx@sha256:33e0bbc7ca9ecf108140af6288c7c9d1ecc77548cbfd3952fd8466a75edefe57
Port: <none>
Host Port: <none>
State: Running
Started: Fri, 29 Aug 2025 19:26:41 +0200
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-9bgxx (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-9bgxx:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 24m default-scheduler Successfully assigned default/nginx-depl-68c944fcbc-2xbvq to minikube
Normal Pulling 24m kubelet Pulling image "nginx"
Normal Pulled 24m kubelet Successfully pulled image "nginx" in 11.204s (11.204s including waiting). Image size: 192385800 bytes.
Normal Created 24m kubelet Created container: nginx
Normal Started 24m kubelet Started container nginx
```
Pod copies are called a 'replicaset'.
# Create
Create a 'deployment' of `nginx`.
```sh
name="nginx-depl"
kubectl create deployment ${name} --image=nginx
kubectl get deployments
```
The command did not specify a namespace, so `default` is used.
# Update
Update a deployment, with `$EDITOR`.
```sh
kubectl edit deployments.apps ${name}
```
This gives us far too much information:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "1"
[ ... ]
creationTimestamp: "2025-08-29T18:13:45Z"
generation: 3
labels:
app: nginx-depl
name: nginx-depl
namespace: default
resourceVersion: "17696"
uid: 8dec2925-5c34-4635-b82c-ba601cb3bef5
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx-depl
[ ... ]
observedGeneration: 3
readyReplicas: 2
replicas: 2
updatedReplicas: 2
```
Pull out the information, without an `$EDITOR`:
```sh
file="webstuff"
kubectl get deployment ${name} -o yaml > ${webstuff}.yaml
```
## Enter the Pod
```bash
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-depl-68c944fcbc-2xbvq 1/1 Running 0 31m
$ pod='nginx-depl-68c944fcbc-2xbvq'
$ kubectl exec -it ${pod} -- bash
root@nginx-depl-68c944fcbc-2xbvq:/# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
root@nginx-depl-68c944fcbc-2xbvq:/#
exit
```
# Delete
Delete a deployment, and watch it leave:
```sh
name=nginx-depl
kubectl delete deployments.apps nginx-depl && kubectl get deployments.apps
kubectl get deployments.apps
```

View File

@@ -0,0 +1,64 @@
---
title: "Kubernetes Docs"
tags: [ "virtualization", "kubernetes", "WTFM", "hosts", "DNS" ]
requires: [ "Kubernetes Basics" ]
---
`kubectl` provides easy high-level overviews:
```sh
kubectl config view
kubectl cluster-info
```
Unfortunately, the documentation commands work less well.
# Kube-Explain
The `kubectl explain` resources cannot use tab-completion.
But you can find the same resources listed with `api-resources`, and use a fuzzy-finder, to get the same effect.
```sh
t="$(kubectl api-resources | fzy | gawk '{print $1}')"
kubectl explain ${t}
```
The documentation is in a classic style, which could only be improved by deletion.
Take this fragment from `kubectl explain namespaces`:
```
DESCRIPTION:
Namespace provides a scope for Names. Use of multiple namespaces is
optional.
```
- Sentence 1: A 'name-space' is a space for names.
- Sentence 2: If you create a namespace, you will not receive an error due to not having a second namespace. This is also the case for deployments, variables, and files which begin with the letter 'P', but the writer was being paid per word.
Continuing...
```
metadata <ObjectMeta>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
```
So a namespace has metadata, which has something in diamond-brackets called `ObjectMeta`.
We should understand this as 'normal metadata for an object', and the author promises us more information.
But only a fool would trust that link, as the author's a hack, without even the flimsy excuse of being left alone with ChatGPT (the docs existed before LLMs became uncool).
Completing this morality tale, entitled '*Why Nobody Reads the Docs*', we arrive at the end of Kubernetes' namespace documentation:
```
status <NamespaceStatus>
Status describes the current status of a Namespace. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
```
Of course this is wrong.
The sentence should read:
```
'Status' describes the current status of a Namespace.
```

View File

@@ -0,0 +1,53 @@
---
title: "Proxy API"
tags: [ "virtualization", "kubernetes" ]
requires: [ "Kubernetes Basics" ]
---
Start the proxy:
```sh
kubectl proxy &
```
Then curl the API server:
```sh
curl http://localhost:8001
```
Create a token:
```sh
export TOKEN=$(kubectl create token default)
kubectl create clusterrole api-access-root \
--verb=get --non-resource-url=/*
kubectl create clusterrolebinding api-access-root \
--clusterrole api-access-root --serviceaccount=default:default
```
Retrieve the API Server endpoint:
```sh
export APISERVER=$(kubectl config view | grep https | \
cut -f 2- -d ":" | tr -d " ")
```
Confirm that the `APISERVER` variable stored the same IP as the Kubernetes control plane IP by issuing the following two commands and comparing their outputs:
```sh
echo $APISERVER
https://192.168.99.100:8443
kubectl cluster-info
```
We can also get the certs straight from the list in `~/.kube/config`.

View File

@@ -0,0 +1,54 @@
---
title: "Kubernetes Setup"
tags: [ "virtualization", "kubernetes", "minikube", "docker" ]
---
# Install `minikube`
Set up a practice environment with `minikube`, using either Docker or VirtualBox.
1. Install the driver (VirtualBox is a good choice).
1. Install `minikube`.
* Debian requires manual installation.[^minideb]
1. Check it works.
The installation takes a long time.
```sh
# driver=docker
driver=virtualbox
minikube start --driver=${driver}
```
## Check `minikube`
Check it's all running:
```sh
minikube kubectl -- get po -A
```
```
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-674b8bbfcf-l7582 1/1 Running 0 2m
kube-system etcd-minikube 1/1 Running 0 2m
kube-system kube-apiserver-minikube 1/1 Running 0 2m
kube-system kube-controller-manager-minikube 1/1 Running 0 2m
kube-system kube-proxy-4q977 1/1 Running 0 2m
kube-system kube-scheduler-minikube 1/1 Running 0 2m
kube-system storage-provisioner 1/1 Running 1 (2m ago) 2m
```
# Uninstall `minikube`
```sh
du -sh ~/.minikube
```
Minikube is huge!
```sh
minikube stop
rm -rf ~/.minikube
```
[^minideb]: https://minikube.sigs.k8s.io/docs/start/