Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
d7c9e217be
|
@@ -35,6 +35,10 @@ type ResolutionSpec struct {
|
||||
// Address to be queried for IP
|
||||
// +required
|
||||
Address string `json:"address"`
|
||||
|
||||
// Resolver can be used to specify another resolver instead of the hosts default resolver
|
||||
// +optional
|
||||
Resolver *string `json:"resolver,omitempty"`
|
||||
}
|
||||
|
||||
// ResolutionStatus defines the observed state of Resolution.
|
||||
@@ -63,11 +67,16 @@ type ResolutionStatus struct {
|
||||
// +kubebuilder:title=`IP Addresses`
|
||||
// +optional
|
||||
IPAddresses []net.IP `json:"ipAddresses,omitempty"`
|
||||
|
||||
// Resolver used for this lookup
|
||||
// +optional
|
||||
Resolver *string `json:"resolver,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.spec.address`
|
||||
// +kubebuilder:printcolumn:name="Resolver",type=string,JSONPath=`.spec.resolver`
|
||||
// +kubebuilder:printcolumn:name="IPs",type=string,JSONPath=`.status.ipAddresses`
|
||||
|
||||
// Resolution is the Schema for the resolutions API
|
||||
|
||||
@@ -35,6 +35,11 @@ type ResolverSpec struct {
|
||||
// IPAddress is the main field for a resolver. This will be used to run queries against.
|
||||
// +required
|
||||
IPAddress net.IP `json:"ipAddress"`
|
||||
|
||||
// Port used for dns lookup queries, will default to 53 if omitted
|
||||
// +kubebuilder:default=53
|
||||
// +optional
|
||||
Port uint `json:"port"`
|
||||
}
|
||||
|
||||
// ResolverStatus defines the observed state of Resolver.
|
||||
|
||||
@@ -31,7 +31,7 @@ func (in *Resolution) DeepCopyInto(out *Resolution) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
@@ -88,6 +88,11 @@ func (in *ResolutionList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResolutionSpec) DeepCopyInto(out *ResolutionSpec) {
|
||||
*out = *in
|
||||
if in.Resolver != nil {
|
||||
in, out := &in.Resolver, &out.Resolver
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolutionSpec.
|
||||
@@ -121,6 +126,11 @@ func (in *ResolutionStatus) DeepCopyInto(out *ResolutionStatus) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if in.Resolver != nil {
|
||||
in, out := &in.Resolver, &out.Resolver
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolutionStatus.
|
||||
|
||||
@@ -18,6 +18,9 @@ spec:
|
||||
- jsonPath: .spec.address
|
||||
name: Address
|
||||
type: string
|
||||
- jsonPath: .spec.resolver
|
||||
name: Resolver
|
||||
type: string
|
||||
- jsonPath: .status.ipAddresses
|
||||
name: IPs
|
||||
type: string
|
||||
@@ -49,6 +52,10 @@ spec:
|
||||
address:
|
||||
description: Address to be queried for IP
|
||||
type: string
|
||||
resolver:
|
||||
description: Resolver can be used to specify another resolver instead
|
||||
of the hosts default resolver
|
||||
type: string
|
||||
required:
|
||||
- address
|
||||
type: object
|
||||
@@ -131,6 +138,9 @@ spec:
|
||||
type: string
|
||||
title: IP Addresses
|
||||
type: array
|
||||
resolver:
|
||||
description: Resolver used for this lookup
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
|
||||
@@ -43,6 +43,11 @@ spec:
|
||||
description: IPAddress is the main field for a resolver. This will
|
||||
be used to run queries against.
|
||||
type: string
|
||||
port:
|
||||
default: 53
|
||||
description: Port used for dns lookup queries, will default to 53
|
||||
if omitted
|
||||
type: integer
|
||||
required:
|
||||
- ipAddress
|
||||
type: object
|
||||
|
||||
@@ -5,4 +5,4 @@ kind: Kustomization
|
||||
images:
|
||||
- name: controller
|
||||
newName: git.stinnesbeck.com/k8s/dns-operator
|
||||
newTag: v0.0.1
|
||||
newTag: v0.0.22
|
||||
|
||||
@@ -28,3 +28,4 @@ metadata:
|
||||
name: whatismyip
|
||||
spec:
|
||||
address: whatismyip.com
|
||||
resolver: fw
|
||||
|
||||
@@ -8,3 +8,13 @@ metadata:
|
||||
name: cloudflare
|
||||
spec:
|
||||
ipAddress: 1.1.1.1
|
||||
---
|
||||
apiVersion: dns.stinnesbeck.com/v1
|
||||
kind: Resolver
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: dns-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: fw
|
||||
spec:
|
||||
ipAddress: 192.168.168.254
|
||||
|
||||
@@ -17,11 +17,11 @@ limitations under the License.
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
dnsv1 "stinnesbeck.com/dns/api/v1"
|
||||
v1 "stinnesbeck.com/dns/api/v1"
|
||||
)
|
||||
|
||||
// ResolutionReconciler reconciles a Resolution object
|
||||
@@ -68,16 +69,31 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
resolver := net.DefaultResolver
|
||||
|
||||
// check if a resolver was given
|
||||
if resolution.Spec.Resolver != nil {
|
||||
customResolver, err := r.getResolver(ctx, req, &resolution)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
resolution.Status.Resolver = resolution.Spec.Resolver
|
||||
resolver = customResolver
|
||||
}
|
||||
|
||||
// Snapshot before mutation
|
||||
patchBase := resolution.DeepCopy()
|
||||
|
||||
addrs, err := net.LookupHost(resolution.Spec.Address)
|
||||
var addrs []string
|
||||
|
||||
addrs, err := resolver.LookupHost(ctx, resolution.Spec.Address)
|
||||
if err != nil {
|
||||
log.Error(err, "error while resolving resolution")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Build new desired status (IMPORTANT: no append)
|
||||
// Build new desired status
|
||||
var ips []net.IP
|
||||
for _, addr := range addrs {
|
||||
if ip := net.ParseIP(addr); ip != nil {
|
||||
@@ -85,24 +101,11 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
}
|
||||
}
|
||||
|
||||
slices.SortStableFunc(ips, func(a, b net.IP) int {
|
||||
for i := range 15 {
|
||||
if a[i] < b[i] {
|
||||
return -1
|
||||
}
|
||||
|
||||
if a[i] > b[i] {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
// sort to make states comparable
|
||||
slices.SortStableFunc(ips, stableSortIPs)
|
||||
|
||||
// check if both ip address slices are the same
|
||||
if slices.EqualFunc(patchBase.Status.IPAddresses, ips, func(s1, s2 net.IP) bool {
|
||||
return bytes.Equal(s1, s2)
|
||||
}) {
|
||||
if slices.EqualFunc(patchBase.Status.IPAddresses, ips, equalIPs) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
@@ -111,30 +114,70 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
resolution.Status.IPAddresses = ips
|
||||
|
||||
// Only patch status (clean + minimal diff)
|
||||
if err := r.Status().Patch(
|
||||
ctx,
|
||||
&resolution,
|
||||
client.MergeFrom(patchBase),
|
||||
); err != nil {
|
||||
if err := r.Status().Patch(ctx, &resolution, client.MergeFrom(patchBase)); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// if err := r.Patch(ctx, &resolution, client.Merge); err != nil {
|
||||
func (r *ResolutionReconciler) getResolver(ctx context.Context, req ctrl.Request, resolution *v1.Resolution) (*net.Resolver, error) {
|
||||
var resolver v1.Resolver
|
||||
|
||||
resolverFQDN := client.ObjectKey{
|
||||
Namespace: req.Namespace,
|
||||
Name: *resolution.Spec.Resolver,
|
||||
}
|
||||
|
||||
if err := r.Get(ctx, resolverFQDN, &resolver); err != nil {
|
||||
// retrieval of resolver failed
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netResolver := net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := net.Dialer{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
return d.DialContext(ctx, network, fmt.Sprintf("%s:%d", resolver.Spec.IPAddress, resolver.Spec.Port))
|
||||
},
|
||||
}
|
||||
|
||||
return &netResolver, nil
|
||||
|
||||
// // Snapshot before mutation
|
||||
// patchBase := resolution.DeepCopy()
|
||||
|
||||
// ip, _ := netResolver.LookupHost(context.Background(), resolution.Spec.Address)
|
||||
|
||||
// if err := r.Status().Patch(ctx, resolution, client.MergeFrom(patchBase)); err != nil {
|
||||
// return ctrl.Result{}, err
|
||||
// }
|
||||
|
||||
// r := &net.Resolver{
|
||||
// PreferGo: true,
|
||||
// Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
// d := net.Dialer{
|
||||
// Timeout: time.Millisecond * time.Duration(10000),
|
||||
// }
|
||||
// return d.DialContext(ctx, network, "8.8.8.8:53")
|
||||
// },
|
||||
// }
|
||||
// ip, _ := r.LookupHost(context.Background(), "www.google.com")
|
||||
// return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func equalIPs(a, b net.IP) bool {
|
||||
return a.Equal(b)
|
||||
}
|
||||
|
||||
func stableSortIPs(a, b net.IP) int {
|
||||
if len(a) != len(b) {
|
||||
a, b = a.To16(), b.To16()
|
||||
}
|
||||
|
||||
for i := range a {
|
||||
if a[i] < b[i] {
|
||||
return -1
|
||||
}
|
||||
|
||||
if a[i] > b[i] {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
|
||||
@@ -69,7 +69,6 @@ func (r *ResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// TODO(user): your logic here
|
||||
switch {
|
||||
case len(resolver.Spec.IPAddress) == 0:
|
||||
// IPAddress is not a valid IP
|
||||
@@ -81,6 +80,17 @@ func (r *ResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
||||
// IP is IPv6
|
||||
}
|
||||
|
||||
// exit loop here if port was provided
|
||||
if resolver.Spec.Port != 0 {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// set defaults and patch it back to k8s
|
||||
resolver.Spec.Port = 53
|
||||
if err := r.Patch(ctx, &resolver, client.Merge); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user