package bind import ( "context" "fmt" "strings" ) // Rndc runs `rndc ` on a pod and returns its output. func (e *Executor) Rndc(ctx context.Context, namespace, pod string, args ...string) (string, error) { base := []string{RndcBin, "-c", RndcConfPath} return e.Exec(ctx, namespace, pod, append(base, args...), "") } // Reconfig reloads named.conf and any newly added/removed zones without a full // restart. func (e *Executor) Reconfig(ctx context.Context, namespace, pod string) error { _, err := e.Rndc(ctx, namespace, pod, "reconfig") return err } // AddZone provisions a zone at runtime via `rndc addzone`. config is the inner // zone clause, e.g. `{ type primary; file "db.example"; allow-update { key k; }; };`. func (e *Executor) AddZone(ctx context.Context, namespace, pod, zone, view, config string) error { args := []string{"addzone", zone} if view != "" { args = append(args, "in", view) } args = append(args, config) out, err := e.Rndc(ctx, namespace, pod, args...) if err != nil { // addzone fails if the zone already exists; fall back to modzone so the // operation is idempotent. if strings.Contains(err.Error(), "already exists") || strings.Contains(out, "already exists") { return e.ModZone(ctx, namespace, pod, zone, view, config) } return err } return nil } // ModZone updates an existing runtime-added zone's configuration. func (e *Executor) ModZone(ctx context.Context, namespace, pod, zone, view, config string) error { args := []string{"modzone", zone} if view != "" { args = append(args, "in", view) } args = append(args, config) _, err := e.Rndc(ctx, namespace, pod, args...) return err } // DelZone removes a runtime-added zone. A missing zone is treated as success. func (e *Executor) DelZone(ctx context.Context, namespace, pod, zone, view string) error { args := []string{"delzone", zone} if view != "" { args = append(args, "in", view) } out, err := e.Rndc(ctx, namespace, pod, args...) if err != nil && (strings.Contains(err.Error(), "not found") || strings.Contains(out, "not found")) { return nil } return err } // ZoneSerial returns the current SOA serial for a zone via `rndc zonestatus`. func (e *Executor) ZoneSerial(ctx context.Context, namespace, pod, zone, view string) (int64, error) { args := []string{"zonestatus", zone} if view != "" { args = append(args, "in", view) } out, err := e.Rndc(ctx, namespace, pod, args...) if err != nil { return 0, err } for _, line := range strings.Split(out, "\n") { line = strings.TrimSpace(line) if strings.HasPrefix(line, "serial:") { var serial int64 if _, err := fmt.Sscanf(line, "serial: %d", &serial); err == nil { return serial, nil } } } return 0, nil }