Skip to content
This repository was archived by the owner on Apr 24, 2024. It is now read-only.

Commit 0fec8ac

Browse files
committed
Implement NetworkPolicy generation
1 parent 696620b commit 0fec8ac

File tree

7 files changed

+158
-79
lines changed

7 files changed

+158
-79
lines changed

api/v1alpha1/dnsresolver_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ type DNSResolverSpec struct {
2424
// +kubebuilder:validation:Required
2525
// +kubebuilder:validation:MaxItems=500
2626
DomainList []string `json:"domainList"`
27-
// +kubebuilder:default=false
27+
// +kubebuilder:default=NetworkPolicy
2828
// +optional
29-
CreateDomainIPMapping bool `json:"createDomainIPMapping"`
29+
GenerateType string `json:"generateType"`
3030
}
3131

3232
type DNSResolverStatus struct {

config/crd/bases/dns.k8s.delta10.nl_dnsresolvers.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ spec:
3232
type: object
3333
spec:
3434
properties:
35-
createDomainIPMapping:
36-
default: false
37-
type: boolean
3835
domainList:
3936
items:
4037
type: string
4138
maxItems: 500
4239
type: array
40+
generateType:
41+
default: NetworkPolicy
42+
type: string
4343
required:
4444
- domainList
4545
type: object

config/manager/kustomization.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ kind: Kustomization
55
images:
66
- name: controller
77
newName: dns-resolution-operator
8-
newTag: v0.1.1-alpha
8+
newTag: v0.1.2-alpha

config/manager/manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ spec:
3535
kubectl.kubernetes.io/default-container: manager
3636
labels:
3737
control-plane: controller-manager
38+
k8s-cache.coredns.io/early-refresh: "true"
3839
spec:
3940
affinity:
4041
nodeAffinity:

internal/controller/dnsresolver_controller.go

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ package controller
1818

1919
import (
2020
"context"
21+
"fmt"
2122
"os"
2223
"strconv"
2324
"time"
2425

2526
"github.com/miekg/dns"
2627
"k8s.io/api/core/v1"
27-
errors "k8s.io/apimachinery/pkg/api/errors"
28+
"k8s.io/apimachinery/pkg/api/errors"
2829
"k8s.io/apimachinery/pkg/runtime"
2930
"k8s.io/apimachinery/pkg/types"
3031
ctrl "sigs.k8s.io/controller-runtime"
3132
"sigs.k8s.io/controller-runtime/pkg/client"
3233
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3334
"sigs.k8s.io/controller-runtime/pkg/log"
35+
networking "k8s.io/api/networking/v1"
3436

3537
dnsv1alpha1 "github.com/delta10/dns-resolution-operator/api/v1alpha1"
3638
)
@@ -40,17 +42,18 @@ type DNSResolverReconciler struct {
4042
Scheme *runtime.Scheme
4143
}
4244

43-
type ipMapOptions struct {
44-
CreateDomainIPMapping bool
45+
type resolverOptions struct {
46+
GenerateType string
4547
}
4648

4749
var Config struct {
48-
EnableIPv6 bool
49-
IPExpiration time.Duration
50-
MaxRequeueTime uint32
51-
DNS *dns.ClientConfig
52-
DNSProtocol string
53-
DNSEnvironment string
50+
EnableIPv6 bool
51+
IPExpiration time.Duration
52+
MaxRequeueTime uint32
53+
DNS *dns.ClientConfig
54+
DNSProtocol string
55+
DNSEnvironment string
56+
UpstreamDNSService string
5457
}
5558

5659
var IPCache IPCacheT
@@ -82,6 +85,12 @@ func init() {
8285
Config.DNSProtocol = "udp"
8386
Config.DNS.Port = "53"
8487
Config.DNSEnvironment = "kubernetes"
88+
s := os.Getenv("DNS_UPSTREAM_SERVICE")
89+
if s == "" {
90+
Config.UpstreamDNSService = "kube-dns"
91+
} else {
92+
Config.UpstreamDNSService = s
93+
}
8594
// We add the servers at the beginning of every Reconcile
8695
}
8796
duration, err := time.ParseDuration(os.Getenv("IP_EXPIRATION"))
@@ -109,22 +118,21 @@ func init() {
109118
// It gets triggered by changes in DNSResolvers
110119
func (r *DNSResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
111120
log := log.FromContext(ctx)
112-
default_result_obj := ctrl.Result{
113-
RequeueAfter: time.Second * 10,
114-
}
121+
// result object used for errors
122+
default_result_obj := ctrl.Result{}
115123

116124
if Config.DNSEnvironment == "kubernetes" {
117125
Config.DNS.Servers = make([]string, 0, 2)
126+
118127
// Obtain the pod IPs of kube-dns
119128
kube_dns_name := types.NamespacedName{
120129
Namespace: "kube-system",
121-
Name: "kube-dns",
130+
Name: Config.UpstreamDNSService,
122131
}
123132
dns_ep := new(v1.Endpoints)
124133
err := r.Get(ctx, kube_dns_name, dns_ep)
125134
if err != nil {
126-
log.Error(err, "unable to fetch kube-dns Endpoints")
127-
return default_result_obj, err
135+
return default_result_obj, fmt.Errorf("unable to fetch kube-dns Endpoints: %w", err)
128136
}
129137
for i := range dns_ep.Subsets {
130138
for _, addr := range dns_ep.Subsets[i].Addresses {
@@ -143,13 +151,13 @@ func (r *DNSResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request)
143151
IPCache.Delete(req.NamespacedName, "")
144152
log.Info("Removed IPMap from cache", "IPMap.Namespace", req.Namespace, "IPMap.Name", req.Name)
145153
} else {
146-
log.Error(err, "unable to fetch DNSResolver")
154+
err = fmt.Errorf("unable to fetch DNSResolver: %w", err)
147155
}
148156
return default_result_obj, client.IgnoreNotFound(err)
149157
}
150158

151-
options := &ipMapOptions{
152-
CreateDomainIPMapping: resolver.Spec.CreateDomainIPMapping,
159+
options := &resolverOptions{
160+
GenerateType: resolver.Spec.GenerateType,
153161
}
154162

155163
// Create an empty IPMap to populate later
@@ -159,6 +167,7 @@ func (r *DNSResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request)
159167

160168
// First, check if `resolver` has an associated IPMap already
161169
get_err := r.Get(ctx, req.NamespacedName, ip_map)
170+
ip_map.ObjectMeta.Labels = resolver.ObjectMeta.Labels
162171

163172
// Make sure the ownerRef on `resolver` is set to the IPMap we are working on
164173
// This will fail if IPMap is already owned by another resource
@@ -168,49 +177,68 @@ func (r *DNSResolverReconciler) Reconcile(ctx context.Context, req ctrl.Request)
168177

169178
if get_err != nil && errors.IsNotFound(get_err) {
170179

171-
// There is no IPMap matching `resolver`, so we create one
172-
173-
log.Info("Creating IPMap", "IPMap.Namespace", req.Namespace, "IPMap.Name", req.Name)
180+
// There is no IPMap matching `resolver`, so we create an IPMap and NetworkPolicy
174181

175182
_, minttl, err := ipmapUpdate(ip_map, resolver.Spec.DomainList, options)
176183
if err != nil {
177-
log.Error(err, "failed to generate IPMap Data")
178-
return default_result_obj, err
184+
return default_result_obj, fmt.Errorf("failed to generate IPMap Data: %w", err)
179185
}
186+
187+
if options.GenerateType == "NetworkPolicy" {
188+
if err := r.NPReconcile(ctx, ip_map); err != nil {
189+
// We return so the IPMap does not get updated. This ensure we take the same code path next time
190+
return default_result_obj, fmt.Errorf("failed to create NetworkPolicy: %w", err)
191+
}
192+
}
193+
194+
log.Info("Creating IPMap", "IPMap.Namespace", req.Namespace, "IPMap.Name", req.Name)
180195
if err := r.Create(ctx, ip_map); err != nil {
181-
log.Error(err, "failed to create IPMap resource")
182-
return default_result_obj, err
196+
return default_result_obj, fmt.Errorf("failed to create IPMap resource: %w", err)
183197
}
184198

185199
requeue := time.Second * time.Duration(minttl+1)
186200
log.Info("Requeueing DNSResolver", "RequeueAfter", requeue)
187201
return ctrl.Result{RequeueAfter: requeue}, nil
188202

189-
} else if get_err != nil {
203+
} else if get_err == nil {
190204

191-
log.Error(get_err, "failed to get IPMap")
192-
return default_result_obj, get_err
193-
194-
} else {
195-
196-
// The IPMap matching `resolver` exists, so we update it
205+
// The IPMap matching `resolver` exists, so we update it and the NetworkPolicy
197206

198207
updated, minttl, err := ipmapUpdate(ip_map, resolver.Spec.DomainList, options)
199208
if err != nil {
200-
log.Error(err, "failed to generate IPMap Data (update)")
201-
return default_result_obj, err
209+
return default_result_obj, fmt.Errorf("failed to generate IPMap Data (update): %w", err)
202210
}
211+
203212
if updated {
213+
if options.GenerateType == "NetworkPolicy" {
214+
if err := r.NPReconcile(ctx, ip_map); err != nil {
215+
// We return so the IPMap does not get updated. This ensure we take the same code path next time
216+
return default_result_obj, fmt.Errorf("failed to update NetworkPolicy: %w", err)
217+
}
218+
}
204219
log.Info("Updating IPMap", "IPMap.Namespace", req.Namespace, "IPMap.Name", req.Name)
205220
if err := r.Update(ctx, ip_map); err != nil {
206-
log.Error(err, "failed to update IPMap resource")
207-
return default_result_obj, err
221+
return default_result_obj, fmt.Errorf("failed to update IPMap resource: %w", err)
222+
}
223+
} else {
224+
if options.GenerateType == "NetworkPolicy" {
225+
// Note that if a NetworkPolicy exists we assume it is synced with the IPMap already. We still
226+
// want to check if it exists in the no-update scenario, in case it was inadvertently deleted
227+
np := new(networking.NetworkPolicy)
228+
name := types.NamespacedName{Name: ip_map.Name, Namespace: ip_map.Namespace}
229+
if err := r.Get(ctx, name, np); err != nil {
230+
if err := r.NPReconcile(ctx, ip_map); err != nil {
231+
return default_result_obj, fmt.Errorf("failed to create NetworkPolicy: %w", err)
232+
}
233+
}
208234
}
209235
}
210236

211237
requeue := time.Second * time.Duration(minttl+1)
212238
log.Info("Requeueing DNSResolver", "RequeueAfter", requeue)
213239
return ctrl.Result{RequeueAfter: requeue}, nil
240+
} else {
241+
return default_result_obj, fmt.Errorf("failed to get IPMap: %w", get_err)
214242
}
215243
}
216244

internal/controller/ipmap.go

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ You may obtain a copy of the License at
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,
1212
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
See the License for the specific language governing permissions and
13+
see the license for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

@@ -159,7 +159,7 @@ func purgeExpired(ip_map *dnsv1alpha1.IPMap) bool {
159159
func ipmapUpdate(
160160
ip_map *dnsv1alpha1.IPMap,
161161
domainList []string,
162-
options *ipMapOptions,
162+
options *resolverOptions,
163163
) (updated bool, minttl uint32, err error) {
164164
debug := ctrl.Log.V(1)
165165
minttl = uint32(Config.MaxRequeueTime)
@@ -173,35 +173,19 @@ func ipmapUpdate(
173173
updated = true
174174
}
175175

176-
if options.CreateDomainIPMapping {
177-
// Remove domains that are no longer requested
178-
OUTER:
179-
for i, old_domain := range ip_map.Data.Domains {
180-
for _, domain := range domainList {
181-
if domain == old_domain.Domain {
182-
continue OUTER
183-
}
176+
// Remove domains that are no longer requested
177+
OUTER:
178+
for i, old_domain := range ip_map.Data.Domains {
179+
for _, domain := range domainList {
180+
if domain == old_domain.Domain {
181+
continue OUTER
184182
}
185-
// not found in requested domains
186-
newlen := len(ip_map.Data.Domains) - 1
187-
ip_map.Data.Domains[i] = ip_map.Data.Domains[newlen]
188-
ip_map.Data.Domains = ip_map.Data.Domains[:newlen]
189-
updated = true
190-
}
191-
} else {
192-
// Make sure there is only a domain "" in the ip_map Data
193-
if len(ip_map.Data.Domains) > 1 {
194-
ip_map.Data = new(dnsv1alpha1.IPMapData)
195-
_, _ = getIpMapDomainOrCreate(ip_map, "")
196-
updated = true
197-
} else if len(ip_map.Data.Domains) == 0 {
198-
_, _ = getIpMapDomainOrCreate(ip_map, "")
199-
updated = true
200-
} else if ip_map.Data.Domains[0].Domain != "" {
201-
ip_map.Data = new(dnsv1alpha1.IPMapData)
202-
_, _ = getIpMapDomainOrCreate(ip_map, "")
203-
updated = true
204183
}
184+
// not found in requested domains
185+
newlen := len(ip_map.Data.Domains) - 1
186+
ip_map.Data.Domains[i] = ip_map.Data.Domains[newlen]
187+
ip_map.Data.Domains = ip_map.Data.Domains[:newlen]
188+
updated = true
205189
}
206190

207191
start_time := time.Now()
@@ -210,16 +194,13 @@ func ipmapUpdate(
210194
if err != nil {
211195
return updated, 0, err
212196
}
197+
213198
for _, domain := range domainList {
214199
var ip_list *dnsv1alpha1.IPList
215-
if options.CreateDomainIPMapping {
216-
index, created := getIpMapDomainOrCreate(ip_map, domain)
217-
ip_list = &ip_map.Data.Domains[index]
218-
if created {
219-
updated = true
220-
}
221-
} else {
222-
ip_list = &ip_map.Data.Domains[0]
200+
index, created := getIpMapDomainOrCreate(ip_map, domain)
201+
ip_list = &ip_map.Data.Domains[index]
202+
if created {
203+
updated = true
223204
}
224205

225206
ips, ttl, err := lookupDomain(domain, conns)

0 commit comments

Comments
 (0)