mirror of
https://github.com/YuzuZensai/netbird-kubernetes-operator.git
synced 2026-09-13 18:59:09 +00:00
Add support for policy auto-creation
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maps"
|
||||||
|
|
||||||
"github.com/netbirdio/kubernetes-operator/internal/util"
|
"github.com/netbirdio/kubernetes-operator/internal/util"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
@@ -18,6 +20,10 @@ type NBResourceSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
PolicyName string `json:"policyName,omitempty"`
|
PolicyName string `json:"policyName,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
|
PolicySourceGroups []string `json:"policySourceGroups,omitempty"`
|
||||||
|
// +optional
|
||||||
|
PolicyFriendlyName map[string]string `json:"policyFriendlyName,omitempty"`
|
||||||
|
// +optional
|
||||||
TCPPorts []int32 `json:"tcpPorts,omitempty"`
|
TCPPorts []int32 `json:"tcpPorts,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
UDPPorts []int32 `json:"udpPorts,omitempty"`
|
UDPPorts []int32 `json:"udpPorts,omitempty"`
|
||||||
@@ -31,7 +37,8 @@ func (a NBResourceSpec) Equal(b NBResourceSpec) bool {
|
|||||||
util.Equivalent(a.Groups, b.Groups) &&
|
util.Equivalent(a.Groups, b.Groups) &&
|
||||||
a.PolicyName == b.PolicyName &&
|
a.PolicyName == b.PolicyName &&
|
||||||
util.Equivalent(a.TCPPorts, b.TCPPorts) &&
|
util.Equivalent(a.TCPPorts, b.TCPPorts) &&
|
||||||
util.Equivalent(a.UDPPorts, b.UDPPorts)
|
util.Equivalent(a.UDPPorts, b.UDPPorts) &&
|
||||||
|
util.Equivalent(a.PolicySourceGroups, b.PolicySourceGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NBResourceStatus defines the observed state of NBResource.
|
// NBResourceStatus defines the observed state of NBResource.
|
||||||
@@ -47,7 +54,13 @@ type NBResourceStatus struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Groups []string `json:"groups,omitempty"`
|
Groups []string `json:"groups,omitempty"`
|
||||||
// +optional
|
// +optional
|
||||||
|
PolicySourceGroups []string `json:"policySourceGroups,omitempty"`
|
||||||
|
// +optional
|
||||||
|
PolicyFriendlyName map[string]string `json:"policyFriendlyName,omitempty"`
|
||||||
|
// +optional
|
||||||
Conditions []NBCondition `json:"conditions,omitempty"`
|
Conditions []NBCondition `json:"conditions,omitempty"`
|
||||||
|
// +optional
|
||||||
|
PolicyNameMapping map[string]string `json:"policyNameMapping"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns if NBResourceStatus is equal to this one
|
// Equal returns if NBResourceStatus is equal to this one
|
||||||
@@ -57,7 +70,10 @@ func (a NBResourceStatus) Equal(b NBResourceStatus) bool {
|
|||||||
util.Equivalent(a.TCPPorts, b.TCPPorts) &&
|
util.Equivalent(a.TCPPorts, b.TCPPorts) &&
|
||||||
util.Equivalent(a.UDPPorts, b.UDPPorts) &&
|
util.Equivalent(a.UDPPorts, b.UDPPorts) &&
|
||||||
util.Equivalent(a.Groups, b.Groups) &&
|
util.Equivalent(a.Groups, b.Groups) &&
|
||||||
util.Equivalent(a.Conditions, b.Conditions)
|
util.Equivalent(a.Conditions, b.Conditions) &&
|
||||||
|
util.Equivalent(a.PolicySourceGroups, b.PolicySourceGroups) &&
|
||||||
|
maps.Equal(a.PolicyFriendlyName, b.PolicyFriendlyName) &&
|
||||||
|
maps.Equal(a.PolicyNameMapping, b.PolicyNameMapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
// +kubebuilder:object:root=true
|
// +kubebuilder:object:root=true
|
||||||
|
|||||||
@@ -329,6 +329,18 @@ func (in *NBResourceSpec) DeepCopyInto(out *NBResourceSpec) {
|
|||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.PolicySourceGroups != nil {
|
||||||
|
in, out := &in.PolicySourceGroups, &out.PolicySourceGroups
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.PolicyFriendlyName != nil {
|
||||||
|
in, out := &in.PolicyFriendlyName, &out.PolicyFriendlyName
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
if in.TCPPorts != nil {
|
if in.TCPPorts != nil {
|
||||||
in, out := &in.TCPPorts, &out.TCPPorts
|
in, out := &in.TCPPorts, &out.TCPPorts
|
||||||
*out = make([]int32, len(*in))
|
*out = make([]int32, len(*in))
|
||||||
@@ -379,6 +391,18 @@ func (in *NBResourceStatus) DeepCopyInto(out *NBResourceStatus) {
|
|||||||
*out = make([]string, len(*in))
|
*out = make([]string, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.PolicySourceGroups != nil {
|
||||||
|
in, out := &in.PolicySourceGroups, &out.PolicySourceGroups
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.PolicyFriendlyName != nil {
|
||||||
|
in, out := &in.PolicyFriendlyName, &out.PolicyFriendlyName
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
if in.Conditions != nil {
|
if in.Conditions != nil {
|
||||||
in, out := &in.Conditions, &out.Conditions
|
in, out := &in.Conditions, &out.Conditions
|
||||||
*out = make([]NBCondition, len(*in))
|
*out = make([]NBCondition, len(*in))
|
||||||
@@ -386,6 +410,13 @@ func (in *NBResourceStatus) DeepCopyInto(out *NBResourceStatus) {
|
|||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if in.PolicyNameMapping != nil {
|
||||||
|
in, out := &in.PolicyNameMapping, &out.PolicyNameMapping
|
||||||
|
*out = make(map[string]string, len(*in))
|
||||||
|
for key, val := range *in {
|
||||||
|
(*out)[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NBResourceStatus.
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NBResourceStatus.
|
||||||
|
|||||||
+19
-10
@@ -67,12 +67,13 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
// NB Specific flags
|
// NB Specific flags
|
||||||
var (
|
var (
|
||||||
managementURL string
|
managementURL string
|
||||||
clientImage string
|
clientImage string
|
||||||
clusterName string
|
clusterName string
|
||||||
namespacedNetworks bool
|
namespacedNetworks bool
|
||||||
clusterDNS string
|
clusterDNS string
|
||||||
netbirdAPIKey string
|
netbirdAPIKey string
|
||||||
|
allowAutomaticPolicyCreation bool
|
||||||
)
|
)
|
||||||
flag.StringVar(&managementURL, "netbird-management-url", "https://api.netbird.io", "Management service URL")
|
flag.StringVar(&managementURL, "netbird-management-url", "https://api.netbird.io", "Management service URL")
|
||||||
flag.StringVar(&clientImage, "netbird-client-image", "netbirdio/netbird:latest", "Image for netbird client container")
|
flag.StringVar(&clientImage, "netbird-client-image", "netbirdio/netbird:latest", "Image for netbird client container")
|
||||||
@@ -90,6 +91,12 @@ func main() {
|
|||||||
)
|
)
|
||||||
flag.StringVar(&clusterDNS, "cluster-dns", "svc.cluster.local", "Cluster DNS name")
|
flag.StringVar(&clusterDNS, "cluster-dns", "svc.cluster.local", "Cluster DNS name")
|
||||||
flag.StringVar(&netbirdAPIKey, "netbird-api-key", "", "API key for NetBird API operations")
|
flag.StringVar(&netbirdAPIKey, "netbird-api-key", "", "API key for NetBird API operations")
|
||||||
|
flag.BoolVar(
|
||||||
|
&allowAutomaticPolicyCreation,
|
||||||
|
"allow-automatic-policy-creation",
|
||||||
|
false,
|
||||||
|
"Allow creating NBPolicy resources from annotations on Services",
|
||||||
|
)
|
||||||
|
|
||||||
// Controller generic flags
|
// Controller generic flags
|
||||||
var (
|
var (
|
||||||
@@ -233,10 +240,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = (&controller.NBResourceReconciler{
|
if err = (&controller.NBResourceReconciler{
|
||||||
Client: mgr.GetClient(),
|
Client: mgr.GetClient(),
|
||||||
Scheme: mgr.GetScheme(),
|
Scheme: mgr.GetScheme(),
|
||||||
APIKey: netbirdAPIKey,
|
APIKey: netbirdAPIKey,
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
|
AllowAutomaticPolicyCreation: allowAutomaticPolicyCreation,
|
||||||
|
ClusterName: clusterName,
|
||||||
}).SetupWithManager(mgr); err != nil {
|
}).SetupWithManager(mgr); err != nil {
|
||||||
setupLog.Error(err, "unable to create controller", "controller", "NBResource")
|
setupLog.Error(err, "unable to create controller", "controller", "NBResource")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ apiVersion: v2
|
|||||||
name: kubernetes-operator
|
name: kubernetes-operator
|
||||||
description: NetBird Kubernetes Operator
|
description: NetBird Kubernetes Operator
|
||||||
type: application
|
type: application
|
||||||
version: 0.1.7
|
version: 0.1.8
|
||||||
appVersion: "0.1.2"
|
appVersion: "0.1.3"
|
||||||
|
|||||||
@@ -55,8 +55,16 @@ spec:
|
|||||||
x-kubernetes-validations:
|
x-kubernetes-validations:
|
||||||
- message: Value is immutable
|
- message: Value is immutable
|
||||||
rule: self == oldSelf
|
rule: self == oldSelf
|
||||||
|
policyFriendlyName:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
policyName:
|
policyName:
|
||||||
type: string
|
type: string
|
||||||
|
policySourceGroups:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
tcpPorts:
|
tcpPorts:
|
||||||
items:
|
items:
|
||||||
format: int32
|
format: int32
|
||||||
@@ -116,8 +124,20 @@ spec:
|
|||||||
type: array
|
type: array
|
||||||
networkResourceID:
|
networkResourceID:
|
||||||
type: string
|
type: string
|
||||||
|
policyFriendlyName:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
policyName:
|
policyName:
|
||||||
type: string
|
type: string
|
||||||
|
policyNameMapping:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
policySourceGroups:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
tcpPorts:
|
tcpPorts:
|
||||||
items:
|
items:
|
||||||
format: int32
|
format: int32
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ spec:
|
|||||||
{{- if or .Values.netbirdAPI.key .Values.netbirdAPI.keyFromSecret }}
|
{{- if or .Values.netbirdAPI.key .Values.netbirdAPI.keyFromSecret }}
|
||||||
- --netbird-api-key=$(NB_API_KEY)
|
- --netbird-api-key=$(NB_API_KEY)
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- if .Values.ingress.allowAutomaticPolicyCreation }}
|
||||||
|
- --allow-automatic-policy-creation
|
||||||
|
{{- end }}
|
||||||
ports:
|
ports:
|
||||||
- name: webhook-server
|
- name: webhook-server
|
||||||
containerPort: {{ .Values.webhook.service.port }}
|
containerPort: {{ .Values.webhook.service.port }}
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ ingress:
|
|||||||
enabled: false
|
enabled: false
|
||||||
# Create router per namespace, useful for strict networking requirements
|
# Create router per namespace, useful for strict networking requirements
|
||||||
namespacedNetworks: false
|
namespacedNetworks: false
|
||||||
|
# Allow creating policies through Service annotations
|
||||||
|
allowAutomaticPolicyCreation: false
|
||||||
kubernetesAPI:
|
kubernetesAPI:
|
||||||
enabled: false
|
enabled: false
|
||||||
groups: []
|
groups: []
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ var (
|
|||||||
errUnknownProtocol = fmt.Errorf("Unknown protocol")
|
errUnknownProtocol = fmt.Errorf("Unknown protocol")
|
||||||
errKubernetesAPI = fmt.Errorf("kubernetes API error")
|
errKubernetesAPI = fmt.Errorf("kubernetes API error")
|
||||||
errNetBirdAPI = fmt.Errorf("netbird API error")
|
errNetBirdAPI = fmt.Errorf("netbird API error")
|
||||||
|
errInvalidValue = fmt.Errorf("invalid value")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -77,16 +78,28 @@ func (r *NBPolicyReconciler) mapResources(ctx context.Context, nbPolicy *netbird
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
if resource.Status.PolicyName != nil && util.Contains(util.SplitTrim(*resource.Status.PolicyName, ","), nbPolicy.Name) {
|
generatedBy := nbPolicy.Annotations["netbird.io/generated-by"]
|
||||||
// Groups
|
generatedBy = strings.ReplaceAll(generatedBy, "/", "-")
|
||||||
groups = append(groups, resource.Status.Groups...)
|
if resource.Status.PolicyName == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resourcePolicies := util.SplitTrim(*resource.Status.PolicyName, ",")
|
||||||
|
|
||||||
for _, p := range resource.Spec.TCPPorts {
|
if generatedBy == "" && !util.Contains(resourcePolicies, nbPolicy.Name) {
|
||||||
portMapping[protocolTCP][p] = nil
|
continue
|
||||||
}
|
}
|
||||||
for _, p := range resource.Spec.UDPPorts {
|
|
||||||
portMapping[protocolUDP][p] = nil
|
if generatedBy != "" && !util.Contains(resourcePolicies, strings.ReplaceAll(nbPolicy.Name, "-"+generatedBy, "")) {
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
// Groups
|
||||||
|
groups = append(groups, resource.Status.Groups...)
|
||||||
|
|
||||||
|
for _, p := range resource.Spec.TCPPorts {
|
||||||
|
portMapping[protocolTCP][p] = nil
|
||||||
|
}
|
||||||
|
for _, p := range resource.Spec.UDPPorts {
|
||||||
|
portMapping[protocolUDP][p] = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
"github.com/go-logr/logr"
|
"github.com/go-logr/logr"
|
||||||
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
|
||||||
@@ -25,10 +26,12 @@ import (
|
|||||||
// NBResourceReconciler reconciles a NBResource object
|
// NBResourceReconciler reconciles a NBResource object
|
||||||
type NBResourceReconciler struct {
|
type NBResourceReconciler struct {
|
||||||
client.Client
|
client.Client
|
||||||
Scheme *runtime.Scheme
|
Scheme *runtime.Scheme
|
||||||
APIKey string
|
APIKey string
|
||||||
ManagementURL string
|
ManagementURL string
|
||||||
netbird *netbird.Client
|
AllowAutomaticPolicyCreation bool
|
||||||
|
ClusterName string
|
||||||
|
netbird *netbird.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||||
@@ -96,6 +99,7 @@ func (r *NBResourceReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||||||
err = r.handlePolicy(ctx, req, nbResource, groupIDs, logger)
|
err = r.handlePolicy(ctx, req, nbResource, groupIDs, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
nbResource.Status.Conditions = netbirdiov1.NBConditionFalse("internalError", fmt.Sprintf("Error occurred handling policy changes: %v", err))
|
nbResource.Status.Conditions = netbirdiov1.NBConditionFalse("internalError", fmt.Sprintf("Error occurred handling policy changes: %v", err))
|
||||||
|
return ctrl.Result{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nbResource.Status.Conditions = netbirdiov1.NBConditionTrue()
|
nbResource.Status.Conditions = netbirdiov1.NBConditionTrue()
|
||||||
@@ -103,115 +107,236 @@ func (r *NBResourceReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||||||
return ctrl.Result{}, nil
|
return ctrl.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *NBResourceReconciler) handlePolicyCreate(ctx context.Context, nbResource *netbirdiov1.NBResource, req ctrl.Request, policy string, nbPolicy *netbirdiov1.NBPolicy, logger logr.Logger) error {
|
||||||
|
if len(nbResource.Spec.PolicySourceGroups) == 0 {
|
||||||
|
logger.Error(errInvalidValue, "Cannot auto-generate policy, missing source groups.")
|
||||||
|
return fmt.Errorf("cannot auto-generate policy, missing source groups")
|
||||||
|
}
|
||||||
|
name := nbResource.Spec.PolicyFriendlyName[policy]
|
||||||
|
if name == "" {
|
||||||
|
name = fmt.Sprintf("Autogenerated policy for resource %s/%s in cluster %s", nbResource.Namespace, nbResource.Name, r.ClusterName)
|
||||||
|
}
|
||||||
|
generatedName := fmt.Sprintf("%s-%s-%s", policy, req.Namespace, req.Name)
|
||||||
|
*nbPolicy = netbirdiov1.NBPolicy{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: generatedName,
|
||||||
|
Annotations: map[string]string{"netbird.io/generated-by": req.NamespacedName.String()},
|
||||||
|
Finalizers: []string{"netbird.io/cleanup"},
|
||||||
|
},
|
||||||
|
Spec: netbirdiov1.NBPolicySpec{
|
||||||
|
Name: name,
|
||||||
|
Description: "Generated by " + req.NamespacedName.String(),
|
||||||
|
SourceGroups: nbResource.Spec.PolicySourceGroups,
|
||||||
|
Bidirectional: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.Client.Create(ctx, nbPolicy)
|
||||||
|
if errors.IsAlreadyExists(err) {
|
||||||
|
err = r.Client.Get(ctx, types.NamespacedName{Name: generatedName}, nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nbPolicy.Annotations == nil {
|
||||||
|
nbPolicy.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
nbPolicy.Annotations["netbird.io/generated-by"] = req.NamespacedName.String()
|
||||||
|
nbPolicy.Spec = netbirdiov1.NBPolicySpec{
|
||||||
|
Name: name,
|
||||||
|
Description: "Generated by " + req.NamespacedName.String(),
|
||||||
|
SourceGroups: nbResource.Spec.PolicySourceGroups,
|
||||||
|
Bidirectional: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Client.Update(ctx, nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nbResource.Status.PolicyNameMapping == nil {
|
||||||
|
nbResource.Status.PolicyNameMapping = make(map[string]string)
|
||||||
|
}
|
||||||
|
nbResource.Status.PolicyNameMapping[policy] = generatedName
|
||||||
|
nbResource.Status.PolicySourceGroups = nbResource.Spec.PolicySourceGroups
|
||||||
|
nbResource.Status.PolicyFriendlyName = nbResource.Spec.PolicyFriendlyName
|
||||||
|
|
||||||
|
nbPolicy.Status.ManagedServiceList = append(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
||||||
|
err = r.Client.Status().Update(ctx, nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NBResourceReconciler) handlePolicyAddUpdate(ctx context.Context, req ctrl.Request, nbResource *netbirdiov1.NBResource, policy string, groupIDs []string, logger logr.Logger) error {
|
||||||
|
var nbPolicy netbirdiov1.NBPolicy
|
||||||
|
updatePolicyStatus := false
|
||||||
|
|
||||||
|
kubernetesPolicyName := policy
|
||||||
|
if v, ok := nbResource.Status.PolicyNameMapping[policy]; ok {
|
||||||
|
kubernetesPolicyName = v
|
||||||
|
}
|
||||||
|
err := r.Client.Get(ctx, types.NamespacedName{Name: kubernetesPolicyName}, &nbPolicy)
|
||||||
|
if errors.IsNotFound(err) && r.AllowAutomaticPolicyCreation {
|
||||||
|
err = r.handlePolicyCreate(ctx, nbResource, req, policy, &nbPolicy, logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if errors.IsNotFound(err) && !r.AllowAutomaticPolicyCreation {
|
||||||
|
logger.Info("automatic policy creation is not allowed")
|
||||||
|
return nil
|
||||||
|
} else if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "error getting NBPolicy", "err", err, "policyName", policy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
||||||
|
nbPolicy.Status.ManagedServiceList = append(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
||||||
|
updatePolicyStatus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.Equivalent(nbResource.Spec.TCPPorts, nbResource.Status.TCPPorts) {
|
||||||
|
nbResource.Status.TCPPorts = nbResource.Spec.TCPPorts
|
||||||
|
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
||||||
|
updatePolicyStatus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.Equivalent(nbResource.Spec.UDPPorts, nbResource.Status.UDPPorts) {
|
||||||
|
nbResource.Status.UDPPorts = nbResource.Spec.UDPPorts
|
||||||
|
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
||||||
|
updatePolicyStatus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.Equivalent(nbResource.Status.Groups, groupIDs) {
|
||||||
|
nbResource.Status.Groups = groupIDs
|
||||||
|
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
||||||
|
updatePolicyStatus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := nbResource.Status.PolicyNameMapping[policy]; ok {
|
||||||
|
updatePolicySpec := false
|
||||||
|
if v, ok := nbPolicy.Annotations["netbird.io/generated-by"]; !ok || v != req.NamespacedName.String() {
|
||||||
|
if nbPolicy.Annotations == nil {
|
||||||
|
nbPolicy.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
nbPolicy.Annotations["netbird.io/generated-by"] = req.NamespacedName.String()
|
||||||
|
updatePolicySpec = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := nbResource.Spec.PolicyFriendlyName[policy]; ok {
|
||||||
|
if nbPolicy.Spec.Name != v {
|
||||||
|
nbPolicy.Spec.Name = v
|
||||||
|
updatePolicySpec = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if nbPolicy.Spec.Name != fmt.Sprintf("Autogenerated policy for resource %s/%s in cluster %s", nbResource.Namespace, nbResource.Name, r.ClusterName) {
|
||||||
|
nbPolicy.Spec.Name = fmt.Sprintf("Autogenerated policy for resource %s/%s in cluster %s", nbResource.Namespace, nbResource.Name, r.ClusterName)
|
||||||
|
updatePolicySpec = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nbPolicy.Spec.Description != "Generated by "+req.NamespacedName.String() {
|
||||||
|
nbPolicy.Spec.Description = "Generated by " + req.NamespacedName.String()
|
||||||
|
updatePolicySpec = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util.Equivalent(nbPolicy.Spec.SourceGroups, nbResource.Spec.PolicySourceGroups) {
|
||||||
|
nbPolicy.Spec.SourceGroups = nbResource.Spec.PolicySourceGroups
|
||||||
|
updatePolicySpec = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if updatePolicySpec {
|
||||||
|
err := r.Client.Update(ctx, &nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if updatePolicyStatus {
|
||||||
|
err := r.Client.Status().Update(ctx, &nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "error updating NBPolicy", "err", err, "policyName", policy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *NBResourceReconciler) handlePolicyDelete(ctx context.Context, req ctrl.Request, nbResource *netbirdiov1.NBResource, specPolicies []string, policy string, logger logr.Logger) error {
|
||||||
|
var nbPolicy netbirdiov1.NBPolicy
|
||||||
|
if !util.Contains(specPolicies, policy) {
|
||||||
|
kubeName := policy
|
||||||
|
if v, ok := nbResource.Status.PolicyNameMapping[policy]; ok {
|
||||||
|
kubeName = v
|
||||||
|
}
|
||||||
|
err := r.Client.Get(ctx, types.NamespacedName{Name: kubeName}, &nbPolicy)
|
||||||
|
if !errors.IsNotFound(err) {
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "error getting NBPolicy", "err", err, "policyName", policy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := nbResource.Status.PolicyNameMapping[policy]; ok {
|
||||||
|
// Delete Policy
|
||||||
|
err := r.Client.Delete(ctx, &nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "error deleting NBPolicy", "err", err, "policyName", policy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(nbResource.Status.PolicyNameMapping, policy)
|
||||||
|
} else if util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
||||||
|
nbPolicy.Status.ManagedServiceList = util.Without(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
||||||
|
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
||||||
|
err := r.Client.Status().Update(ctx, &nbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(errKubernetesAPI, "error updating NBPolicy", "err", err, "policyName", policy)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handlePolicy update NBPolicy if defined to add self reference to policy status
|
// handlePolicy update NBPolicy if defined to add self reference to policy status
|
||||||
func (r *NBResourceReconciler) handlePolicy(ctx context.Context, req ctrl.Request, nbResource *netbirdiov1.NBResource, groupIDs []string, logger logr.Logger) error {
|
func (r *NBResourceReconciler) handlePolicy(ctx context.Context, req ctrl.Request, nbResource *netbirdiov1.NBResource, groupIDs []string, logger logr.Logger) error {
|
||||||
if nbResource.Status.PolicyName == nil && nbResource.Spec.PolicyName == "" {
|
if nbResource.Status.PolicyName == nil && nbResource.Spec.PolicyName == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var nbPolicy netbirdiov1.NBPolicy
|
specPolicies := util.SplitTrim(nbResource.Spec.PolicyName, ",")
|
||||||
if nbResource.Spec.PolicyName == "" && nbResource.Status.PolicyName != nil {
|
var statusPolicies []string
|
||||||
// Remove self reference from policy status
|
if nbResource.Status.PolicyName != nil {
|
||||||
policies := util.SplitTrim(*nbResource.Status.PolicyName, ",")
|
statusPolicies = util.SplitTrim(*nbResource.Status.PolicyName, ",")
|
||||||
for _, policyName := range policies {
|
}
|
||||||
err := r.Client.Get(ctx, types.NamespacedName{Name: policyName}, &nbPolicy)
|
|
||||||
nbResource.Status.PolicyName = nil
|
for _, policy := range specPolicies {
|
||||||
if err != nil {
|
err := r.handlePolicyAddUpdate(ctx, req, nbResource, policy, groupIDs, logger)
|
||||||
logger.Error(errKubernetesAPI, "error getting NBPolicy", "err", err, "policyName", policyName)
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
if util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
|
||||||
nbPolicy.Status.ManagedServiceList = util.Without(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
|
||||||
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
|
||||||
err := r.Client.Status().Update(ctx, &nbPolicy)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errKubernetesAPI, "error updating NBPolicy", "err", err, "policyName", policyName)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
specPolicies := util.SplitTrim(nbResource.Spec.PolicyName, ",")
|
|
||||||
var statusPolicies []string
|
for _, policy := range statusPolicies {
|
||||||
if nbResource.Status.PolicyName != nil {
|
err := r.handlePolicyDelete(ctx, req, nbResource, specPolicies, policy, logger)
|
||||||
statusPolicies = util.SplitTrim(*nbResource.Status.PolicyName, ",")
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, policy := range specPolicies {
|
if nbResource.Status.PolicyName == nil || *nbResource.Status.PolicyName != nbResource.Spec.PolicyName {
|
||||||
updatePolicyStatus := false
|
nbResource.Status.PolicyName = &nbResource.Spec.PolicyName
|
||||||
|
|
||||||
err := r.Client.Get(ctx, types.NamespacedName{Name: policy}, &nbPolicy)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errKubernetesAPI, "error getting NBPolicy", "err", err, "policyName", policy)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !util.Contains(statusPolicies, policy) {
|
|
||||||
// New
|
|
||||||
if !util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
|
||||||
nbPolicy.Status.ManagedServiceList = append(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
|
||||||
updatePolicyStatus = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Check update
|
|
||||||
if !util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
|
||||||
nbPolicy.Status.ManagedServiceList = append(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
|
||||||
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
|
||||||
updatePolicyStatus = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !util.Equivalent(nbResource.Spec.TCPPorts, nbResource.Status.TCPPorts) {
|
|
||||||
nbResource.Status.TCPPorts = nbResource.Spec.TCPPorts
|
|
||||||
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
|
||||||
updatePolicyStatus = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !util.Equivalent(nbResource.Spec.UDPPorts, nbResource.Status.UDPPorts) {
|
|
||||||
nbResource.Status.UDPPorts = nbResource.Spec.UDPPorts
|
|
||||||
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
|
||||||
updatePolicyStatus = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !util.Equivalent(nbResource.Status.Groups, groupIDs) {
|
|
||||||
nbResource.Status.Groups = groupIDs
|
|
||||||
nbPolicy.Status.LastUpdatedAt = &v1.Time{Time: time.Now()}
|
|
||||||
updatePolicyStatus = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if updatePolicyStatus {
|
|
||||||
err := r.Client.Status().Update(ctx, &nbPolicy)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errKubernetesAPI, "error updating NBPolicy", "err", err, "policyName", policy)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, policy := range statusPolicies {
|
|
||||||
// Delete
|
|
||||||
if !util.Contains(specPolicies, policy) {
|
|
||||||
err := r.Client.Get(ctx, types.NamespacedName{Name: policy}, &nbPolicy)
|
|
||||||
if !errors.IsNotFound(err) {
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errKubernetesAPI, "error getting NBPolicy", "err", err, "policyName", policy)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if util.Contains(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String()) {
|
|
||||||
nbPolicy.Status.ManagedServiceList = util.Without(nbPolicy.Status.ManagedServiceList, req.NamespacedName.String())
|
|
||||||
err := r.Client.Status().Update(ctx, &nbPolicy)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(errKubernetesAPI, "error updating NBPolicy", "err", err, "policyName", policy)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if nbResource.Status.PolicyName == nil || *nbResource.Status.PolicyName != nbResource.Spec.PolicyName {
|
|
||||||
nbResource.Status.PolicyName = &nbResource.Spec.PolicyName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -510,5 +635,18 @@ func (r *NBResourceReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
|||||||
For(&netbirdiov1.NBResource{}).
|
For(&netbirdiov1.NBResource{}).
|
||||||
Named("nbresource").
|
Named("nbresource").
|
||||||
Watches(&netbirdiov1.NBGroup{}, handler.EnqueueRequestForOwner(r.Scheme, mgr.GetRESTMapper(), &netbirdiov1.NBResource{})).
|
Watches(&netbirdiov1.NBGroup{}, handler.EnqueueRequestForOwner(r.Scheme, mgr.GetRESTMapper(), &netbirdiov1.NBResource{})).
|
||||||
|
Watches(&netbirdiov1.NBPolicy{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||||
|
if v, ok := obj.GetAnnotations()["netbird.io/generated-by"]; ok {
|
||||||
|
return []reconcile.Request{
|
||||||
|
{
|
||||||
|
NamespacedName: types.NamespacedName{
|
||||||
|
Namespace: strings.Split(v, "/")[0],
|
||||||
|
Name: strings.Split(v, "/")[1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})).
|
||||||
Complete(r)
|
Complete(r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
var _ = Describe("NBResource Controller", func() {
|
var _ = Describe("NBResource Controller", func() {
|
||||||
Context("When reconciling a resource", func() {
|
Context("When reconciling a resource", func() {
|
||||||
const resourceName = "test-resource"
|
const resourceName = "test-resource"
|
||||||
|
const policyGenName = "test-gen"
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
@@ -45,9 +46,10 @@ var _ = Describe("NBResource Controller", func() {
|
|||||||
server = httptest.NewServer(mux)
|
server = httptest.NewServer(mux)
|
||||||
netbirdClient = netbird.New(server.URL, "ABC")
|
netbirdClient = netbird.New(server.URL, "ABC")
|
||||||
controllerReconciler = &NBResourceReconciler{
|
controllerReconciler = &NBResourceReconciler{
|
||||||
Client: k8sClient,
|
Client: k8sClient,
|
||||||
Scheme: k8sClient.Scheme(),
|
Scheme: k8sClient.Scheme(),
|
||||||
netbird: netbirdClient,
|
netbird: netbirdClient,
|
||||||
|
ClusterName: "kubernetes",
|
||||||
}
|
}
|
||||||
|
|
||||||
By("creating the custom resource for the Kind NBResource")
|
By("creating the custom resource for the Kind NBResource")
|
||||||
@@ -315,65 +317,48 @@ var _ = Describe("NBResource Controller", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
When("Policy is specified", Ordered, func() {
|
When("Policy is specified", Ordered, func() {
|
||||||
BeforeAll(func() {
|
When("Policy Exists", func() {
|
||||||
nbPolicy := &netbirdiov1.NBPolicy{
|
BeforeAll(func() {
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
nbPolicy := &netbirdiov1.NBPolicy{
|
||||||
Name: "test-a",
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
},
|
Name: "test-a",
|
||||||
Spec: netbirdiov1.NBPolicySpec{
|
},
|
||||||
Name: "Test A",
|
Spec: netbirdiov1.NBPolicySpec{
|
||||||
SourceGroups: []string{"All"},
|
Name: "Test A",
|
||||||
},
|
SourceGroups: []string{"All"},
|
||||||
}
|
},
|
||||||
Expect(k8sClient.Create(ctx, nbPolicy)).To(Succeed())
|
}
|
||||||
|
Expect(k8sClient.Create(ctx, nbPolicy)).To(Succeed())
|
||||||
|
|
||||||
nbPolicy = &netbirdiov1.NBPolicy{
|
nbPolicy = &netbirdiov1.NBPolicy{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "test-b",
|
Name: "test-b",
|
||||||
},
|
},
|
||||||
Spec: netbirdiov1.NBPolicySpec{
|
Spec: netbirdiov1.NBPolicySpec{
|
||||||
Name: "Test B",
|
Name: "Test B",
|
||||||
SourceGroups: []string{"All"},
|
SourceGroups: []string{"All"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Expect(k8sClient.Create(ctx, nbPolicy)).To(Succeed())
|
Expect(k8sClient.Create(ctx, nbPolicy)).To(Succeed())
|
||||||
})
|
|
||||||
|
|
||||||
AfterAll(func() {
|
|
||||||
nbPolicy := &netbirdiov1.NBPolicy{}
|
|
||||||
err := k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)
|
|
||||||
if !errors.IsNotFound(err) {
|
|
||||||
Expect(k8sClient.Delete(ctx, nbPolicy)).To(Succeed())
|
|
||||||
}
|
|
||||||
|
|
||||||
nbPolicy = &netbirdiov1.NBPolicy{}
|
|
||||||
err = k8sClient.Get(ctx, types.NamespacedName{Name: "test-b"}, nbPolicy)
|
|
||||||
if !errors.IsNotFound(err) {
|
|
||||||
Expect(k8sClient.Delete(ctx, nbPolicy)).To(Succeed())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
It("should update policy status", func() {
|
|
||||||
nbresource.Spec.PolicyName = "test-a"
|
|
||||||
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
|
||||||
|
|
||||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
|
||||||
NamespacedName: typeNamespacedName,
|
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
nbPolicy := &netbirdiov1.NBPolicy{}
|
AfterAll(func() {
|
||||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
err := k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)
|
||||||
})
|
if !errors.IsNotFound(err) {
|
||||||
|
Expect(k8sClient.Delete(ctx, nbPolicy)).To(Succeed())
|
||||||
|
}
|
||||||
|
|
||||||
When("Policy is updated", func() {
|
nbPolicy = &netbirdiov1.NBPolicy{}
|
||||||
It("should remove old reference and add new reference", func() {
|
err = k8sClient.Get(ctx, types.NamespacedName{Name: "test-b"}, nbPolicy)
|
||||||
nbresource.Spec.PolicyName = "test-b"
|
if !errors.IsNotFound(err) {
|
||||||
|
Expect(k8sClient.Delete(ctx, nbPolicy)).To(Succeed())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
It("should update policy status", func() {
|
||||||
|
nbresource.Spec.PolicyName = "test-a"
|
||||||
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
nbresource.Status.PolicyName = util.Ptr("test-a")
|
|
||||||
Expect(k8sClient.Status().Update(ctx, nbresource)).To(Succeed())
|
|
||||||
|
|
||||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
NamespacedName: typeNamespacedName,
|
NamespacedName: typeNamespacedName,
|
||||||
})
|
})
|
||||||
@@ -381,35 +366,257 @@ var _ = Describe("NBResource Controller", func() {
|
|||||||
|
|
||||||
nbPolicy := &netbirdiov1.NBPolicy{}
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
||||||
Expect(nbPolicy.Status.ManagedServiceList).NotTo(ContainElement("default/test-resource"))
|
|
||||||
|
|
||||||
nbPolicy = &netbirdiov1.NBPolicy{}
|
|
||||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-b"}, nbPolicy)).To(Succeed())
|
|
||||||
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
When("Policy is updated", func() {
|
||||||
|
It("should remove old reference and add new reference", func() {
|
||||||
|
nbresource.Spec.PolicyName = "test-b"
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
nbresource.Status.PolicyName = util.Ptr("test-a")
|
||||||
|
Expect(k8sClient.Status().Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).NotTo(ContainElement("default/test-resource"))
|
||||||
|
|
||||||
|
nbPolicy = &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-b"}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Policy is removed", func() {
|
||||||
|
It("should remove old reference", func() {
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
||||||
|
nbPolicy.Status.ManagedServiceList = []string{"default/test-resource"}
|
||||||
|
Expect(k8sClient.Status().Update(ctx, nbPolicy)).To(Succeed())
|
||||||
|
|
||||||
|
nbresource.Spec.PolicyName = ""
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
nbresource.Status.PolicyName = util.Ptr("test-a")
|
||||||
|
Expect(k8sClient.Status().Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
nbPolicy = &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).NotTo(ContainElement("default/test-resource"))
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("Policy is removed", func() {
|
When("Policy doesn't exist", func() {
|
||||||
It("should remove old reference", func() {
|
When("Policy auto-creation is enabled", func() {
|
||||||
nbPolicy := &netbirdiov1.NBPolicy{}
|
BeforeEach(func() {
|
||||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
controllerReconciler.AllowAutomaticPolicyCreation = true
|
||||||
nbPolicy.Status.ManagedServiceList = []string{"default/test-resource"}
|
})
|
||||||
Expect(k8sClient.Status().Update(ctx, nbPolicy)).To(Succeed())
|
AfterEach(func() {
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
nbresource.Spec.PolicyName = ""
|
err := k8sClient.Get(ctx, types.NamespacedName{Name: "test-gen-" + nbresource.Namespace + "-" + nbresource.Name}, nbPolicy)
|
||||||
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
if !errors.IsNotFound(err) {
|
||||||
|
if len(nbPolicy.Finalizers) > 0 {
|
||||||
nbresource.Status.PolicyName = util.Ptr("test-a")
|
nbPolicy.Finalizers = nil
|
||||||
Expect(k8sClient.Status().Update(ctx, nbresource)).To(Succeed())
|
Expect(k8sClient.Update(ctx, nbPolicy)).To(Succeed())
|
||||||
|
}
|
||||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
err = k8sClient.Delete(ctx, nbPolicy)
|
||||||
NamespacedName: typeNamespacedName,
|
if !errors.IsNotFound(err) {
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
nbPolicy = &netbirdiov1.NBPolicy{}
|
It("should create policy", func() {
|
||||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: "test-a"}, nbPolicy)).To(Succeed())
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
Expect(nbPolicy.Status.ManagedServiceList).NotTo(ContainElement("default/test-resource"))
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Source groups is not defined", func() {
|
||||||
|
It("should return error", func() {
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = nil
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Friendly name is specified", func() {
|
||||||
|
It("should override policy name", func() {
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
nbresource.Spec.PolicyFriendlyName = map[string]string{policyGenName: "UnitTest"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
|
Expect(nbPolicy.Spec.Name).To(Equal("UnitTest"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Policy already exists", func() {
|
||||||
|
It("should update it", func() {
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "test-gen-default-test-resource",
|
||||||
|
},
|
||||||
|
Spec: netbirdiov1.NBPolicySpec{
|
||||||
|
Name: "Test",
|
||||||
|
Description: "Test",
|
||||||
|
SourceGroups: []string{"toast"},
|
||||||
|
Bidirectional: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Expect(k8sClient.Create(ctx, nbPolicy)).To(Succeed())
|
||||||
|
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy = &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
|
Expect(nbPolicy.Spec.Name).To(Equal("Autogenerated policy for resource default/test-resource in cluster kubernetes"))
|
||||||
|
Expect(nbPolicy.Spec.Bidirectional).To(BeTrue())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Policy settings are updated", func() {
|
||||||
|
It("should update it", func() {
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
nbresource.Spec.PolicyFriendlyName = map[string]string{policyGenName: "UnitTest"}
|
||||||
|
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err = controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Status.ManagedServiceList).To(ContainElement("default/test-resource"))
|
||||||
|
Expect(nbPolicy.Spec.Name).To(Equal("UnitTest"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Policy is changed outside controller", func() {
|
||||||
|
It("should update it", func() {
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
nbPolicy.Spec.Name = "Meow"
|
||||||
|
nbPolicy.Annotations = nil
|
||||||
|
nbPolicy.Spec.Description = "woeM"
|
||||||
|
nbPolicy.Spec.SourceGroups = []string{"est"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbPolicy)).To(Succeed())
|
||||||
|
|
||||||
|
_, err = controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.Spec.Name).To(Equal("Autogenerated policy for resource default/test-resource in cluster kubernetes"))
|
||||||
|
Expect(nbPolicy.Annotations["netbird.io/generated-by"]).To(Equal("default/test-resource"))
|
||||||
|
Expect(nbPolicy.Spec.Description).To(Equal("Generated by default/test-resource"))
|
||||||
|
Expect(nbPolicy.Spec.SourceGroups).To(BeEquivalentTo([]string{"test"}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
When("Policy is removed", func() {
|
||||||
|
It("should delete NBPolicy", func() {
|
||||||
|
nbresource.Spec.PolicyName = policyGenName
|
||||||
|
nbresource.Spec.PolicySourceGroups = []string{"test"}
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbresource)).To(Succeed())
|
||||||
|
Expect(nbresource.Status.PolicyNameMapping).To(HaveKey(policyGenName))
|
||||||
|
|
||||||
|
nbPolicy := &netbirdiov1.NBPolicy{}
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
nbresource.Spec.PolicyName = ""
|
||||||
|
Expect(k8sClient.Update(ctx, nbresource)).To(Succeed())
|
||||||
|
|
||||||
|
_, err = controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: nbresource.Status.PolicyNameMapping[policyGenName]}, nbPolicy)).To(Succeed())
|
||||||
|
Expect(nbPolicy.DeletionTimestamp).NotTo(BeNil())
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,12 +31,14 @@ type ServiceReconciler struct {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// ServiceExposeAnnotation Service annotation for exposing
|
// ServiceExposeAnnotation Service annotation for exposing
|
||||||
ServiceExposeAnnotation = "netbird.io/expose"
|
ServiceExposeAnnotation = "netbird.io/expose"
|
||||||
serviceGroupsAnnotation = "netbird.io/groups"
|
serviceGroupsAnnotation = "netbird.io/groups"
|
||||||
serviceResourceAnnotation = "netbird.io/resource-name"
|
serviceResourceAnnotation = "netbird.io/resource-name"
|
||||||
servicePolicyAnnotation = "netbird.io/policy"
|
servicePolicyAnnotation = "netbird.io/policy"
|
||||||
servicePortsAnnotation = "netbird.io/policy-ports"
|
servicePortsAnnotation = "netbird.io/policy-ports"
|
||||||
serviceProtocolAnnotation = "netbird.io/policy-protocol"
|
serviceProtocolAnnotation = "netbird.io/policy-protocol"
|
||||||
|
servicePolicySourceGroupsAnnotation = "netbird.io/policy-source-groups"
|
||||||
|
servicePolicyNameAnnotation = "netbird.io/policy-name"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -241,6 +243,23 @@ func (r *ServiceReconciler) applyPolicy(nbResource *netbirdiov1.NBResource, svc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := svc.Annotations[servicePolicySourceGroupsAnnotation]; ok {
|
||||||
|
nbResource.Spec.PolicySourceGroups = util.SplitTrim(v, ",")
|
||||||
|
} else {
|
||||||
|
nbResource.Spec.PolicySourceGroups = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
policyFriendlyNameList := util.SplitTrim(svc.Annotations[servicePolicyNameAnnotation], ",")
|
||||||
|
nbResource.Spec.PolicyFriendlyName = make(map[string]string)
|
||||||
|
for _, v := range policyFriendlyNameList {
|
||||||
|
friendlyNameMap := util.SplitTrim(v, ":")
|
||||||
|
if len(friendlyNameMap) != 2 {
|
||||||
|
logger.Info("Invalid number of : found in annotation", "annotation", servicePolicyNameAnnotation, "value", v)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nbResource.Spec.PolicyFriendlyName[friendlyNameMap[0]] = friendlyNameMap[1]
|
||||||
|
}
|
||||||
|
|
||||||
for _, p := range svc.Spec.Ports {
|
for _, p := range svc.Spec.Ports {
|
||||||
switch p.Protocol {
|
switch p.Protocol {
|
||||||
case corev1.ProtocolTCP:
|
case corev1.ProtocolTCP:
|
||||||
|
|||||||
@@ -440,6 +440,48 @@ var _ = Describe("Service Controller", func() {
|
|||||||
Expect(nbResource.Spec.UDPPorts).To(BeEmpty())
|
Expect(nbResource.Spec.UDPPorts).To(BeEmpty())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
When("policy friendly name changes", func() {
|
||||||
|
It("should update policy friendly name in NBResource spec", func() {
|
||||||
|
nbResource := &netbirdiov1.NBResource{}
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbResource)).To(Succeed())
|
||||||
|
nbResource.Spec.PolicyName = policyName
|
||||||
|
Expect(k8sClient.Update(ctx, nbResource)).To(Succeed())
|
||||||
|
|
||||||
|
service.Annotations[servicePolicyAnnotation] = policyName
|
||||||
|
service.Annotations[servicePolicyNameAnnotation] = "test:toast,meow:meow"
|
||||||
|
Expect(k8sClient.Update(ctx, service)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
nbResource = &netbirdiov1.NBResource{}
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbResource)).To(Succeed())
|
||||||
|
Expect(nbResource.Spec.PolicyFriendlyName).To(BeEquivalentTo(map[string]string{"test": "toast", "meow": "meow"}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
When("policy source groups changes", func() {
|
||||||
|
It("should update policy source groups in NBResource spec", func() {
|
||||||
|
nbResource := &netbirdiov1.NBResource{}
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbResource)).To(Succeed())
|
||||||
|
nbResource.Spec.PolicyName = policyName
|
||||||
|
Expect(k8sClient.Update(ctx, nbResource)).To(Succeed())
|
||||||
|
|
||||||
|
service.Annotations[servicePolicyAnnotation] = policyName
|
||||||
|
service.Annotations[servicePolicySourceGroupsAnnotation] = "test"
|
||||||
|
Expect(k8sClient.Update(ctx, service)).To(Succeed())
|
||||||
|
|
||||||
|
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||||
|
NamespacedName: typeNamespacedName,
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
nbResource = &netbirdiov1.NBResource{}
|
||||||
|
Expect(k8sClient.Get(ctx, typeNamespacedName, nbResource)).To(Succeed())
|
||||||
|
Expect(nbResource.Spec.PolicySourceGroups).To(BeEquivalentTo([]string{"test"}))
|
||||||
|
})
|
||||||
|
})
|
||||||
When("resource name changes", func() {
|
When("resource name changes", func() {
|
||||||
It("should update name in NBResource spec", func() {
|
It("should update name in NBResource spec", func() {
|
||||||
service.Annotations[serviceResourceAnnotation] = "meow"
|
service.Annotations[serviceResourceAnnotation] = "meow"
|
||||||
|
|||||||
Reference in New Issue
Block a user