4092a25f4f
Uses internetsystemsconsortium/bind9 as the default base image instead of a self-hosted one, verified against internetsystemsconsortium/bind9:9.20 (runs as root; named/rndc/nsupdate at /usr/sbin,/usr/sbin,/usr/bin). - project operator config at /etc/bind-operator instead of overmounting the image's /etc/bind (keeps bind.keys / base config intact) - reference named/rndc/nsupdate by absolute path (exec PATH may exclude /usr/sbin) - centralise filesystem + binary paths in internal/bind/consts.go - default spec.image to internetsystemsconsortium/bind9:9.20
64 lines
1.9 KiB
Go
64 lines
1.9 KiB
Go
// Package bind contains helpers for driving BIND9 pods: executing rndc and
|
|
// nsupdate over the Kubernetes exec subresource, and rendering named.conf.
|
|
package bind
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/client-go/rest"
|
|
"k8s.io/client-go/tools/remotecommand"
|
|
)
|
|
|
|
// Executor runs commands inside BIND pods via the exec subresource.
|
|
type Executor struct {
|
|
config *rest.Config
|
|
clientset kubernetes.Interface
|
|
}
|
|
|
|
// NewExecutor builds an Executor from a controller-runtime rest config.
|
|
func NewExecutor(cfg *rest.Config) (*Executor, error) {
|
|
cs, err := kubernetes.NewForConfig(cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("build clientset: %w", err)
|
|
}
|
|
return &Executor{config: cfg, clientset: cs}, nil
|
|
}
|
|
|
|
// Exec runs command in the BIND container of pod, optionally feeding stdin, and
|
|
// returns stdout. A non-zero exit or transport error yields an error that
|
|
// includes stderr.
|
|
func (e *Executor) Exec(ctx context.Context, namespace, pod string, command []string, stdin string) (string, error) {
|
|
req := e.clientset.CoreV1().RESTClient().Post().
|
|
Resource("pods").
|
|
Name(pod).
|
|
Namespace(namespace).
|
|
SubResource("exec").
|
|
VersionedParams(&corev1.PodExecOptions{
|
|
Container: ContainerName,
|
|
Command: command,
|
|
Stdin: stdin != "",
|
|
Stdout: true,
|
|
Stderr: true,
|
|
}, scheme.ParameterCodec)
|
|
|
|
exec, err := remotecommand.NewSPDYExecutor(e.config, "POST", req.URL())
|
|
if err != nil {
|
|
return "", fmt.Errorf("spdy executor: %w", err)
|
|
}
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
opts := remotecommand.StreamOptions{Stdout: &stdout, Stderr: &stderr}
|
|
if stdin != "" {
|
|
opts.Stdin = bytes.NewBufferString(stdin)
|
|
}
|
|
if err := exec.StreamWithContext(ctx, opts); err != nil {
|
|
return stdout.String(), fmt.Errorf("exec %v: %w (stderr: %s)", command, err, stderr.String())
|
|
}
|
|
return stdout.String(), nil
|
|
}
|