Working manually configured firewall group entries
This commit is contained in:
89
api/v1beta1/firewallgroup_types.go
Normal file
89
api/v1beta1/firewallgroup_types.go
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2025 Vegard Engen.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// FirewallGroupSpec defines the desired state of FirewallGroup.
|
||||
type FirewallGroupSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
|
||||
// Foo is an example field of FirewallGroup. Edit firewallgroup_types.go to remove/update
|
||||
// Description is a human-readable explanation for the object
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
MatchServicesInAllNamespaces bool `json:"matchServicesInAllNamespaces,omitempty"`
|
||||
// ManualAddresses is a list of manual IPs or CIDRs (IPv4 or IPv6)
|
||||
// +optional
|
||||
ManualAddresses []string `json:"manualAddresses,omitempty"`
|
||||
|
||||
// AutoIncludeSelector defines which services to extract addresses from
|
||||
// +optional
|
||||
AutoIncludeSelector *metav1.LabelSelector `json:"autoIncludeSelector,omitempty"`
|
||||
|
||||
// AddressType can be "ip", "cidr", or "both"
|
||||
// +kubebuilder:validation:Enum=ip;cidr;both
|
||||
// +optional
|
||||
AddressType string `json:"addressType,omitempty"`
|
||||
}
|
||||
|
||||
// FirewallGroupStatus defines the observed state of FirewallGroup.
|
||||
type FirewallGroupStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
|
||||
ResolvedAddresses []string `json:"resolvedAddresses,omitempty"`
|
||||
|
||||
// SyncedWithUnifi indicates whether the addresses are successfully pushed
|
||||
// +optional
|
||||
SyncedWithUnifi bool `json:"syncedWithUnifi,omitempty"`
|
||||
|
||||
// LastSyncTime is the last time the object was synced
|
||||
// +optional
|
||||
LastSyncTime *metav1.Time `json:"lastSyncTime,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
|
||||
// FirewallGroup is the Schema for the firewallgroups API.
|
||||
type FirewallGroup struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec FirewallGroupSpec `json:"spec,omitempty"`
|
||||
Status FirewallGroupStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// FirewallGroupList contains a list of FirewallGroup.
|
||||
type FirewallGroupList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []FirewallGroup `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&FirewallGroup{}, &FirewallGroupList{})
|
||||
}
|
||||
@@ -21,9 +21,118 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FirewallGroup) DeepCopyInto(out *FirewallGroup) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FirewallGroup.
|
||||
func (in *FirewallGroup) DeepCopy() *FirewallGroup {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FirewallGroup)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FirewallGroup) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FirewallGroupList) DeepCopyInto(out *FirewallGroupList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]FirewallGroup, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FirewallGroupList.
|
||||
func (in *FirewallGroupList) DeepCopy() *FirewallGroupList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FirewallGroupList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *FirewallGroupList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FirewallGroupSpec) DeepCopyInto(out *FirewallGroupSpec) {
|
||||
*out = *in
|
||||
if in.ManualAddresses != nil {
|
||||
in, out := &in.ManualAddresses, &out.ManualAddresses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AutoIncludeSelector != nil {
|
||||
in, out := &in.AutoIncludeSelector, &out.AutoIncludeSelector
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FirewallGroupSpec.
|
||||
func (in *FirewallGroupSpec) DeepCopy() *FirewallGroupSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FirewallGroupSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FirewallGroupStatus) DeepCopyInto(out *FirewallGroupStatus) {
|
||||
*out = *in
|
||||
if in.ResolvedAddresses != nil {
|
||||
in, out := &in.ResolvedAddresses, &out.ResolvedAddresses
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.LastSyncTime != nil {
|
||||
in, out := &in.LastSyncTime, &out.LastSyncTime
|
||||
*out = (*in).DeepCopy()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FirewallGroupStatus.
|
||||
func (in *FirewallGroupStatus) DeepCopy() *FirewallGroupStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FirewallGroupStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Networkconfiguration) DeepCopyInto(out *Networkconfiguration) {
|
||||
*out = *in
|
||||
|
||||
@@ -223,6 +223,15 @@ func main() {
|
||||
}
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err = (&controller.FirewallGroupReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
UnifiClient: unifiClient,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "FirewallGroup")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if metricsCertWatcher != nil {
|
||||
setupLog.Info("Adding metrics certificate watcher to manager")
|
||||
if err := mgr.Add(metricsCertWatcher); err != nil {
|
||||
|
||||
130
config/crd/bases/unifi.engen.priv.no_firewallgroups.yaml
Normal file
130
config/crd/bases/unifi.engen.priv.no_firewallgroups.yaml
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.17.2
|
||||
name: firewallgroups.unifi.engen.priv.no
|
||||
spec:
|
||||
group: unifi.engen.priv.no
|
||||
names:
|
||||
kind: FirewallGroup
|
||||
listKind: FirewallGroupList
|
||||
plural: firewallgroups
|
||||
singular: firewallgroup
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: FirewallGroup is the Schema for the firewallgroups API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: FirewallGroupSpec defines the desired state of FirewallGroup.
|
||||
properties:
|
||||
addressType:
|
||||
description: AddressType can be "ip", "cidr", or "both"
|
||||
enum:
|
||||
- ip
|
||||
- cidr
|
||||
- both
|
||||
type: string
|
||||
autoIncludeSelector:
|
||||
description: AutoIncludeSelector defines which services to extract
|
||||
addresses from
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
manualAddresses:
|
||||
description: ManualAddresses is a list of manual IPs or CIDRs (IPv4
|
||||
or IPv6)
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
matchServicesInAllNamespaces:
|
||||
type: boolean
|
||||
name:
|
||||
description: |-
|
||||
Foo is an example field of FirewallGroup. Edit firewallgroup_types.go to remove/update
|
||||
Description is a human-readable explanation for the object
|
||||
type: string
|
||||
type: object
|
||||
status:
|
||||
description: FirewallGroupStatus defines the observed state of FirewallGroup.
|
||||
properties:
|
||||
lastSyncTime:
|
||||
description: LastSyncTime is the last time the object was synced
|
||||
format: date-time
|
||||
type: string
|
||||
resolvedAddresses:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
syncedWithUnifi:
|
||||
description: SyncedWithUnifi indicates whether the addresses are successfully
|
||||
pushed
|
||||
type: boolean
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -7,6 +7,7 @@ rules:
|
||||
- apiGroups:
|
||||
- unifi.engen.priv.no
|
||||
resources:
|
||||
- firewallgroups
|
||||
- networkconfigurations
|
||||
verbs:
|
||||
- create
|
||||
@@ -19,12 +20,14 @@ rules:
|
||||
- apiGroups:
|
||||
- unifi.engen.priv.no
|
||||
resources:
|
||||
- firewallgroups/finalizers
|
||||
- networkconfigurations/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- unifi.engen.priv.no
|
||||
resources:
|
||||
- firewallgroups/status
|
||||
- networkconfigurations/status
|
||||
verbs:
|
||||
- get
|
||||
|
||||
17
config/samples/unifi_v1beta1_firewallgroup.yaml
Normal file
17
config/samples/unifi_v1beta1_firewallgroup.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
apiVersion: unifi.engen.priv.no/v1beta1
|
||||
kind: FirewallGroup
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: unifi-network-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: firewallgroup-sample
|
||||
spec:
|
||||
name: Test
|
||||
manualAddresses:
|
||||
- 192.168.1.153
|
||||
- 192.168.1.154
|
||||
- 192.168.1.155
|
||||
- 2a01::3
|
||||
- 2a01:0::5
|
||||
- 2a01:2a01::/32
|
||||
# TODO(user): Add fields here
|
||||
186
internal/controller/firewallgroup_controller.go
Normal file
186
internal/controller/firewallgroup_controller.go
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright 2025 Vegard Engen.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1"
|
||||
goUnifi "github.com/vegardengen/go-unifi/unifi"
|
||||
"github.com/vegardengen/unifi-network-operator/internal/unifi"
|
||||
)
|
||||
|
||||
// FirewallGroupReconciler reconciles a FirewallGroup object
|
||||
type FirewallGroupReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
UnifiClient *unifi.UnifiClient
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=unifi.engen.priv.no,resources=firewallgroups/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the FirewallGroup object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.2/pkg/reconcile
|
||||
|
||||
|
||||
|
||||
func (r *FirewallGroupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := log.FromContext(ctx)
|
||||
var nwObj unifiv1beta1.FirewallGroup
|
||||
if err := r.Get(ctx, req.NamespacedName, &nwObj); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
log.Info(nwObj.Spec.Name)
|
||||
var ipv4, ipv6 []string
|
||||
|
||||
for _,addressEntry := range nwObj.Spec.ManualAddresses {
|
||||
ip := net.ParseIP(addressEntry)
|
||||
|
||||
if ip != nil {
|
||||
if ip.To4() != nil {
|
||||
log.Info(fmt.Sprintf("IPv4 address: %s", addressEntry))
|
||||
ipv4 = append(ipv4, addressEntry)
|
||||
} else {
|
||||
log.Info(fmt.Sprintf("IPv6 address: %s", addressEntry))
|
||||
ipv6 = append(ipv6, ip.String())
|
||||
}
|
||||
} else {
|
||||
addr, net, err := net.ParseCIDR(addressEntry)
|
||||
if err == nil && addr.Equal(net.IP) {
|
||||
if addr.To4() != nil {
|
||||
log.Info(fmt.Sprintf("Ipv4 Net: %s", net))
|
||||
ipv4 = append(ipv4, addressEntry)
|
||||
} else {
|
||||
mask,_ := net.Mask.Size()
|
||||
log.Info(fmt.Sprintf("Ipv6 Net: %s", net))
|
||||
ipv6 = append(ipv6, addr.Mask(net.Mask).String() + "/" + fmt.Sprint(mask))
|
||||
}
|
||||
} else {
|
||||
log.Error(err,fmt.Sprintf("Could not parse: %s", addressEntry))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
firewall_groups, err := r.UnifiClient.Client.ListFirewallGroup(context.Background(), r.UnifiClient.SiteID)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not list network objects")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
ipv4_name := "k8s-"+nwObj.Spec.Name+"-ipv4"
|
||||
ipv6_name := "k8s-"+nwObj.Spec.Name+"-ipv6"
|
||||
ipv4_done := false
|
||||
ipv6_done := false
|
||||
for _,firewall_group := range firewall_groups {
|
||||
if firewall_group.Name == ipv4_name {
|
||||
if(len(ipv4) == 0) {
|
||||
log.Info(fmt.Sprintf("Delete %s", ipv4_name))
|
||||
err := r.UnifiClient.Client.DeleteFirewallGroup(context.Background(), r.UnifiClient.SiteID, firewall_group.ID)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not delete firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
ipv4_done = true
|
||||
} else {
|
||||
if !reflect.DeepEqual(firewall_group.GroupMembers, ipv4) {
|
||||
firewall_group.GroupMembers = ipv4
|
||||
log.Info(fmt.Sprintf("Updating %s", ipv4_name))
|
||||
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not update firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
ipv4_done = true
|
||||
}
|
||||
}
|
||||
if firewall_group.Name == ipv6_name {
|
||||
if(len(ipv6) == 0) {
|
||||
log.Info(fmt.Sprintf("Delete %s", ipv6_name))
|
||||
err := r.UnifiClient.Client.DeleteFirewallGroup(context.Background(), r.UnifiClient.SiteID, firewall_group.ID)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not delete firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
ipv6_done = true
|
||||
} else {
|
||||
if !reflect.DeepEqual(firewall_group.GroupMembers, ipv6) {
|
||||
firewall_group.GroupMembers = ipv6
|
||||
log.Info(fmt.Sprintf("Updating %s", ipv6_name))
|
||||
_, err := r.UnifiClient.Client.UpdateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not update firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
ipv6_done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(ipv4) > 0 && !ipv4_done {
|
||||
log.Info(fmt.Sprintf("Creating %s", ipv4_name))
|
||||
var firewall_group goUnifi.FirewallGroup
|
||||
firewall_group.Name=ipv4_name
|
||||
firewall_group.SiteID=r.UnifiClient.SiteID
|
||||
firewall_group.GroupMembers = ipv4
|
||||
firewall_group.GroupType = "address-group"
|
||||
_, err := r.UnifiClient.Client.CreateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not create firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
if len(ipv6) > 0 && !ipv6_done {
|
||||
log.Info(fmt.Sprintf("Creating %s", ipv6_name))
|
||||
var firewall_group goUnifi.FirewallGroup
|
||||
firewall_group.Name=ipv6_name
|
||||
firewall_group.SiteID=r.UnifiClient.SiteID
|
||||
firewall_group.GroupMembers = ipv6
|
||||
firewall_group.GroupType = "ipv6-address-group"
|
||||
_, err := r.UnifiClient.Client.CreateFirewallGroup(context.Background(), r.UnifiClient.SiteID, &firewall_group)
|
||||
if err != nil {
|
||||
log.Error(err,"Could not create firewall group")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *FirewallGroupReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&unifiv1beta1.FirewallGroup{}).
|
||||
Named("firewallgroup").
|
||||
Complete(r)
|
||||
}
|
||||
84
internal/controller/firewallgroup_controller_test.go
Normal file
84
internal/controller/firewallgroup_controller_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2025 Vegard Engen.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
unifiv1beta1 "github.com/vegardengen/unifi-network-operator/api/v1beta1"
|
||||
)
|
||||
|
||||
var _ = Describe("FirewallGroup Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
const resourceName = "test-resource"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
typeNamespacedName := types.NamespacedName{
|
||||
Name: resourceName,
|
||||
Namespace: "default", // TODO(user):Modify as needed
|
||||
}
|
||||
firewallgroup := &unifiv1beta1.FirewallGroup{}
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating the custom resource for the Kind FirewallGroup")
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, firewallgroup)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
resource := &unifiv1beta1.FirewallGroup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: "default",
|
||||
},
|
||||
// TODO(user): Specify other spec details if needed.
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||
resource := &unifiv1beta1.FirewallGroup{}
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Cleanup the specific resource instance FirewallGroup")
|
||||
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||
})
|
||||
It("should successfully reconcile the resource", func() {
|
||||
By("Reconciling the created resource")
|
||||
controllerReconciler := &FirewallGroupReconciler{
|
||||
Client: k8sClient,
|
||||
Scheme: k8sClient.Scheme(),
|
||||
}
|
||||
|
||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: typeNamespacedName,
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user