yq ist das Schweizer Messer für YAML Dateien

Leider gibt es zwei Programme die den Namen yq besitzen und auch den selben Zweck erfüllen. Beide Programme behandeln YAML Dateien auf der Konsole.

In diesem Artikel beschreibe ich die Version von Mike Farah. Für Arch basierende Distributionen wie Manjaro gibt es yq allerdings noch nicht.

Das PKGBUILD

Leider gibt es im AUR Repository noch keinen Eintrag, sodass ich kurzerhand das folgende PKGBUILD zusammengestrikt habe.

# Maintainer: Sascha Pfau

pkgname=yq
pkgdesc="portable command-line YAML processor written in Go"
pkgver=3.3.4
pkgrel=0
arch=('i686' 'x86_64')
url="https://github.com/mikefarah/yq"
license=('MIT')
makedepends=('go' 'rsync')
source=("https://github.com/mikefarah/yq/archive/${pkgver}.tar.gz")
sha256sums=('b0c44a742a9b6eed25a48ff04bb30e3e05c6c2d5a0c869f9d0d7f778dfd40f05')

# See https://wiki.archlinux.org/index.php/Go_package_guidelines
prepare(){
  cd "$pkgname-$pkgver"
  mkdir -p build
}

build() {
  cd "$pkgname-$pkgver"
  GOPATH="$srcdir" go build -v 
}

package() {
  install -Dm755 "$pkgname-$pkgver"/$pkgname "$pkgdir"/usr/bin/$pkgname
}

Funktionsübersicht

In der folgenden Tabelle sind die grundlegenden Kommandos aufgeführt.

Kommando Beschreibung
read Werte auslesen
validate
compare YAML Dateien vergleichen
write Werte schreibe
create YAML Dateien erstellen
delete
merge
prefix

Bash completion

Kubernetes Konfiguration bearbeiten

Da Kubernetes die Manifeste in YAML Dateien sichert und einliest, eignet sich yq hervoragend um diese zu bearbeiten. Die herkömmlichen Tools awk, sed etc. kommen hier schnell an ihre Grenzen. Zumal yq auch mit multi document YAML Dateien umgehen kann.

Beispiel

In dem Beispiel verwende ich ein Gitea Konfiguration die aus einem Dokument besteht.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: gitea
  namespace: env
  labels:
    name: gitea
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        name: gitea
    spec:
      # Initial rights settings when creating disk can be incorrect
      # for the git user in the gitea container: Use init container to set the rights
      initContainers:
        - name: init-disk
          image: busybox:latest
          command:
            - /bin/chown
            - 1000:1000
            - /data
          volumeMounts:
            - name: gitea
              mountPath: "/data"
      containers:
        - name: gitea
          image: "gitea/gitea:latest"
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
            - containerPort: 22
          volumeMounts:
            - name: gitea
              mountPath: "/data"
            - name: gitea-config
              mountPath: /data/gitea/conf
          resources:
            requests:
              cpu: 10m
              memory: 50Mi
            limits:
              cpu: 1
              memory: 200Mi
      volumes:
        - name: gitea
          persistentVolumeClaim:
            claimName: gitea
        - name: gitea-config
          configMap:
            name: gitea-config
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: gitea-ingress
  namespace: gitea
  annotations:
    traefik.ingress.kubernetes.io/affinity: "true"
spec:
  rules:
    - host: k8s-gitea.domain.duckdns.org
      http:
        paths:
          - backend:
              serviceName: gitea
              servicePort: 443
            path: /
---
apiVersion: v1
kind: Service
metadata:
  name: gitea
  namespace: env
  labels:
    name: gitea
spec:
  ports:
    - name: gitea
      port: 443
      targetPort: 3000
    - name: gitea-ssh
      port: 22
      targetPort: 22
  selector:
    name: gitea
  type: ClusterIP
  sessionAffinity: ClientIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app: gitea
  name: gitea
  namespace: gitea
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Mi
---
apiVersion: v1
kind: Namespace
metadata:
  labels:
    name: gitea
  name: gitea

Für die Nutzung des Manifests sind zuvor noch der Host in dem Ingress angepasst werden.

Host lesen

yq r -d1 install.yaml spec.rules[0].host

Gibt den Value des Hosts aus:

k8s-gitea.domain.duckdns.org  

Host überschreiben

yq w -i -d1 install.yaml spec.rules[0].host k8s-gitea.neuedomain.duckdns.org

Die Ausgabe des Logos von Jooq verhindern

Beim Starten eines Queries wird normalerweise das Logo von Jooq ausgegeben. Das ist auch nicht weiter schlimm, es ist nur beim Debuggen der Anwendung manchmal ein wenig störend, weil es viel Platz auf der Konsole einnimmt.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@  @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@        @@@@@@@@@@
@@@@@@@@@@@@@@@@  @@  @@    @@@@@@@@@@
@@@@@@@@@@  @@@@  @@  @@    @@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@    @@  @@  @@@@  @@@@@@@@@@
@@@@@@@@@@    @@  @@  @@@@  @@@@@@@@@@
@@@@@@@@@@        @@  @  @  @@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  Thank you for using jOOQ 3.11.9

Dank geht an dieser Stelle an Lukas Eder für dieses tolle Werkzeug.

Wie kann nun das Logo abgeschaltet werden? Dazu muss nur die Property org.jooq.no-logo auf true gesetzt werden. Ich löse das in dem ich eine Klasse JooqConfig mit einem Bean erstelle. Die Komponente wird so beim Component Scan gefunden und die Property gesetzt, sodass beim Starten kein Logo angezeigt wird.

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class JooqConfig {
    @Bean
    private void disableLogo() {
        System.setProperty("org.jooq.no-logo", "true");
    }
}

Credentials auslagern

Neben der Möglichkeit die Credentials mit dem Gradle Plugin Credentials zu verwalten, kann man auch zum Beispiel dieses in eine weitere Datei auszulagern. Hierfür bietet sich YAML an.

Hier die jooq.yaml

---
jdbc:
  password: secret
  username: usr

In dem Buildscript jooq.gradle lesen wir die Daten aus dem YAML ein. Das SnakeYaml nutze ich hierfür um die cfg zu definieren.

...
jooq {
  def cfg = new org.yaml.snakeyaml.Yaml().load(new File("" + rootProject.projectDir + "/jooq.yml").newInputStream())
...

Weiter unten im Abschnitt xxx können jetzt die Credentials aus dem jooq.yaml angegeben werden…

...
    jdbc {
      driver = 'org.mariadb.jdbc.Driver'
      url = 'jdbc:mariadb://server:3306/database'
      username = cfg.jdbc.username
      password = cfg.jdbc.password
    }
...