package kubeedit import ( "context" "crypto/sha256" "encoding/hex" "fmt" "strings" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "git.k8s-git-k6n.net/mats/fancy-dns/internal/kubesource" ) // objectName turns a DNS name into a Kubernetes object name. Most names are // already one; a name that is not (an underscore, say) is cleaned and given a // short hash so two different names cannot collide. func objectName(domain string) string { var b strings.Builder changed := false for _, c := range domain { if c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '-' || c == '.' { b.WriteRune(c) } else { b.WriteByte('-') changed = true } } name := strings.Trim(b.String(), "-.") if name == "" { name, changed = "root", true } if changed { sum := sha256.Sum256([]byte(domain)) name += "-" + hex.EncodeToString(sum[:3]) } return name } func (e *Editor) overrideObjects(ctx context.Context) ([]unstructured.Unstructured, error) { return e.list(ctx, kubesource.OverrideGVR) } // Overrides lists the address overrides, one entry per name. func (e *Editor) Overrides(ctx context.Context) ([]kubesource.Override, error) { items, err := e.overrideObjects(ctx) if err != nil { return nil, err } var out []kubesource.Override for i := range items { var s kubesource.AddressOverrideSpec if decodeSpec(&items[i], &s) != nil { continue } if o, err := kubesource.CheckOverride(s); err == nil { out = append(out, o) } } return out, nil } // SetOverride makes domain (and everything under it) answer with address, // replacing whatever it answered with before. func (e *Editor) SetOverride(ctx context.Context, domain, address, actor string) error { o, err := kubesource.CheckOverride(kubesource.AddressOverrideSpec{Domain: domain, Addresses: []string{address}}) if err != nil { return err } name := objectName(o.Domain) items, err := e.overrideObjects(ctx) if err != nil { return err } for i := range items { var s kubesource.AddressOverrideSpec if decodeSpec(&items[i], &s) == nil { if have, err := kubesource.CheckOverride(s); err == nil && have.Domain == o.Domain { name = items[i].GetName() // keep the object an operator may have named by hand break } } } if err := checkName("address override", name); err != nil { return err } _, err = e.upsert(ctx, kubesource.OverrideGVR, "AddressOverride", name, actor, kubesource.AddressOverrideSpec{Domain: o.Domain, Addresses: o.Addresses}) return err } // DeleteOverride removes the override for domain. func (e *Editor) DeleteOverride(ctx context.Context, domain string) error { want := strings.TrimSuffix(strings.ToLower(strings.TrimSpace(domain)), ".") items, err := e.overrideObjects(ctx) if err != nil { return err } removed := false for i := range items { var s kubesource.AddressOverrideSpec if decodeSpec(&items[i], &s) != nil { continue } if have, err := kubesource.CheckOverride(s); err == nil && have.Domain == want { if err := e.remove(ctx, kubesource.OverrideGVR, items[i].GetName()); err != nil { return err } removed = true } } if !removed { return fmt.Errorf("%w: no address override for %s", ErrNotFound, want) } return nil }