more changes
This commit is contained in:
@@ -35,6 +35,10 @@ type ResolutionSpec struct {
|
|||||||
// Address to be queried for IP
|
// Address to be queried for IP
|
||||||
// +required
|
// +required
|
||||||
Address string `json:"address"`
|
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.
|
// ResolutionStatus defines the observed state of Resolution.
|
||||||
@@ -63,11 +67,16 @@ type ResolutionStatus struct {
|
|||||||
// +kubebuilder:title=`IP Addresses`
|
// +kubebuilder:title=`IP Addresses`
|
||||||
// +optional
|
// +optional
|
||||||
IPAddresses []net.IP `json:"ipAddresses,omitempty"`
|
IPAddresses []net.IP `json:"ipAddresses,omitempty"`
|
||||||
|
|
||||||
|
// Resolver used for this lookup
|
||||||
|
// +optional
|
||||||
|
Resolver *string `json:"resolver,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
// +kubebuilder:subresource:status
|
// +kubebuilder:subresource:status
|
||||||
// +kubebuilder:printcolumn:name="Address",type=string,JSONPath=`.spec.address`
|
// +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`
|
// +kubebuilder:printcolumn:name="IPs",type=string,JSONPath=`.status.ipAddresses`
|
||||||
|
|
||||||
// Resolution is the Schema for the resolutions API
|
// 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.
|
// IPAddress is the main field for a resolver. This will be used to run queries against.
|
||||||
// +required
|
// +required
|
||||||
IPAddress net.IP `json:"ipAddress"`
|
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.
|
// ResolverStatus defines the observed state of Resolver.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func (in *Resolution) DeepCopyInto(out *Resolution) {
|
|||||||
*out = *in
|
*out = *in
|
||||||
out.TypeMeta = in.TypeMeta
|
out.TypeMeta = in.TypeMeta
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||||
out.Spec = in.Spec
|
in.Spec.DeepCopyInto(&out.Spec)
|
||||||
in.Status.DeepCopyInto(&out.Status)
|
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.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ResolutionSpec) DeepCopyInto(out *ResolutionSpec) {
|
func (in *ResolutionSpec) DeepCopyInto(out *ResolutionSpec) {
|
||||||
*out = *in
|
*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.
|
// 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.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolutionStatus.
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ spec:
|
|||||||
- jsonPath: .spec.address
|
- jsonPath: .spec.address
|
||||||
name: Address
|
name: Address
|
||||||
type: string
|
type: string
|
||||||
|
- jsonPath: .spec.resolver
|
||||||
|
name: Resolver
|
||||||
|
type: string
|
||||||
- jsonPath: .status.ipAddresses
|
- jsonPath: .status.ipAddresses
|
||||||
name: IPs
|
name: IPs
|
||||||
type: string
|
type: string
|
||||||
@@ -49,6 +52,10 @@ spec:
|
|||||||
address:
|
address:
|
||||||
description: Address to be queried for IP
|
description: Address to be queried for IP
|
||||||
type: string
|
type: string
|
||||||
|
resolver:
|
||||||
|
description: Resolver can be used to specify another resolver instead
|
||||||
|
of the hosts default resolver
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- address
|
- address
|
||||||
type: object
|
type: object
|
||||||
@@ -131,6 +138,9 @@ spec:
|
|||||||
type: string
|
type: string
|
||||||
title: IP Addresses
|
title: IP Addresses
|
||||||
type: array
|
type: array
|
||||||
|
resolver:
|
||||||
|
description: Resolver used for this lookup
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- spec
|
- spec
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ spec:
|
|||||||
description: IPAddress is the main field for a resolver. This will
|
description: IPAddress is the main field for a resolver. This will
|
||||||
be used to run queries against.
|
be used to run queries against.
|
||||||
type: string
|
type: string
|
||||||
|
port:
|
||||||
|
default: 53
|
||||||
|
description: Port used for dns lookup queries, will default to 53
|
||||||
|
if omitted
|
||||||
|
type: integer
|
||||||
required:
|
required:
|
||||||
- ipAddress
|
- ipAddress
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ kind: Kustomization
|
|||||||
images:
|
images:
|
||||||
- name: controller
|
- name: controller
|
||||||
newName: git.stinnesbeck.com/k8s/dns-operator
|
newName: git.stinnesbeck.com/k8s/dns-operator
|
||||||
newTag: v0.0.1
|
newTag: v0.0.22
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ metadata:
|
|||||||
name: whatismyip
|
name: whatismyip
|
||||||
spec:
|
spec:
|
||||||
address: whatismyip.com
|
address: whatismyip.com
|
||||||
|
resolver: fw
|
||||||
|
|||||||
@@ -8,3 +8,13 @@ metadata:
|
|||||||
name: cloudflare
|
name: cloudflare
|
||||||
spec:
|
spec:
|
||||||
ipAddress: 1.1.1.1
|
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
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
|
|
||||||
dnsv1 "stinnesbeck.com/dns/api/v1"
|
dnsv1 "stinnesbeck.com/dns/api/v1"
|
||||||
|
v1 "stinnesbeck.com/dns/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResolutionReconciler reconciles a Resolution object
|
// ResolutionReconciler reconciles a Resolution object
|
||||||
@@ -68,16 +69,31 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||||||
return ctrl.Result{}, err
|
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
|
// Snapshot before mutation
|
||||||
patchBase := resolution.DeepCopy()
|
patchBase := resolution.DeepCopy()
|
||||||
|
|
||||||
addrs, err := net.LookupHost(resolution.Spec.Address)
|
var addrs []string
|
||||||
|
|
||||||
|
addrs, err := resolver.LookupHost(ctx, resolution.Spec.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "error while resolving resolution")
|
log.Error(err, "error while resolving resolution")
|
||||||
return ctrl.Result{}, err
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build new desired status (IMPORTANT: no append)
|
// Build new desired status
|
||||||
var ips []net.IP
|
var ips []net.IP
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
if ip := net.ParseIP(addr); ip != nil {
|
if ip := net.ParseIP(addr); ip != nil {
|
||||||
@@ -85,8 +101,73 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.SortStableFunc(ips, func(a, b net.IP) int {
|
// sort to make states comparable
|
||||||
for i := range 15 {
|
slices.SortStableFunc(ips, stableSortIPs)
|
||||||
|
|
||||||
|
// check if both ip address slices are the same
|
||||||
|
if slices.EqualFunc(patchBase.Status.IPAddresses, ips, equalIPs) {
|
||||||
|
return ctrl.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info(fmt.Sprintf("found the following IP addresses: %v", ips))
|
||||||
|
|
||||||
|
resolution.Status.IPAddresses = ips
|
||||||
|
|
||||||
|
// Only patch status (clean + minimal diff)
|
||||||
|
if err := r.Status().Patch(ctx, &resolution, client.MergeFrom(patchBase)); err != nil {
|
||||||
|
return ctrl.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctrl.Result{}, 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
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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] {
|
if a[i] < b[i] {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
@@ -97,44 +178,6 @@ func (r *ResolutionReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
})
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}) {
|
|
||||||
return ctrl.Result{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info(fmt.Sprintf("found the following IP addresses: %v", ips))
|
|
||||||
|
|
||||||
resolution.Status.IPAddresses = ips
|
|
||||||
|
|
||||||
// Only patch status (clean + minimal diff)
|
|
||||||
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 {
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupWithManager sets up the controller with the Manager.
|
// 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
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(user): your logic here
|
|
||||||
switch {
|
switch {
|
||||||
case len(resolver.Spec.IPAddress) == 0:
|
case len(resolver.Spec.IPAddress) == 0:
|
||||||
// IPAddress is not a valid IP
|
// IPAddress is not a valid IP
|
||||||
@@ -81,6 +80,17 @@ func (r *ResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
|||||||
// IP is IPv6
|
// 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
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user