network: support network isolate
Add new crd to convert kubesphere network policy to k8s network policy, and then other network plugin will do the rest work. Use cache.go from calico project's kube-controller, it aim to sync nsnp with k8s np, delete unused np, and relieve the pressure on k8s restful client. If you want higher performance, you can implement interface NsNetworkPolicyProvider in pkg/controller/provider/namespace_np.go. Signed-off-by: Duan Jiong <djduanjiong@gmail.com>
This commit is contained in:
@@ -5,173 +5,511 @@ import (
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
netv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
typev1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
uruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
v1 "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/klog/klogr"
|
||||
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
|
||||
kubespherescheme "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
|
||||
networkinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
|
||||
networklister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
|
||||
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
workspacev1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
ksnetclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/network/v1alpha1"
|
||||
nspolicy "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
|
||||
workspace "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/controller/network/provider"
|
||||
)
|
||||
|
||||
const controllerAgentName = "nsnp-controller"
|
||||
const (
|
||||
//use period sync service label in NSNP
|
||||
defaultSleepDuration = 60 * time.Second
|
||||
|
||||
type controller struct {
|
||||
kubeClientset kubernetes.Interface
|
||||
kubesphereClientset kubesphereclient.Interface
|
||||
defaultThread = 5
|
||||
defaultSync = "5m"
|
||||
|
||||
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer
|
||||
nsnpLister networklister.NamespaceNetworkPolicyLister
|
||||
nsnpSynced cache.InformerSynced
|
||||
// workqueue is a rate limited work queue. This is used to queue work to be
|
||||
// processed instead of performing it as soon as a change happens. This
|
||||
// means we can ensure we only process a fixed amount of resources at a
|
||||
// time, and makes it easy to ensure we are never processing the same item
|
||||
// simultaneously in two different workers.
|
||||
workqueue workqueue.RateLimitingInterface
|
||||
// recorder is an event recorder for recording Event resources to the
|
||||
// Kubernetes API.
|
||||
recorder record.EventRecorder
|
||||
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider
|
||||
}
|
||||
//whether network isolate is enable in namespace
|
||||
NamespaceNPAnnotationKey = "kubesphere.io/network-isolate"
|
||||
NamespaceNPAnnotationEnabled = "enabled"
|
||||
|
||||
var (
|
||||
log = klogr.New().WithName("Controller").WithValues("Component", controllerAgentName)
|
||||
errCount = 0
|
||||
AnnotationNPNAME = "network-isolate"
|
||||
)
|
||||
|
||||
func NewController(kubeclientset kubernetes.Interface,
|
||||
kubesphereclientset kubesphereclient.Interface,
|
||||
nsnpInformer networkinformer.NamespaceNetworkPolicyInformer,
|
||||
nsNetworkPolicyProvider provider.NsNetworkPolicyProvider) controllerapi.Controller {
|
||||
utilruntime.Must(kubespherescheme.AddToScheme(scheme.Scheme))
|
||||
log.V(4).Info("Creating event broadcaster")
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(klog.Infof)
|
||||
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")})
|
||||
recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName})
|
||||
ctl := &controller{
|
||||
kubeClientset: kubeclientset,
|
||||
kubesphereClientset: kubesphereclientset,
|
||||
nsnpInformer: nsnpInformer,
|
||||
nsnpLister: nsnpInformer.Lister(),
|
||||
nsnpSynced: nsnpInformer.Informer().HasSynced,
|
||||
nsNetworkPolicyProvider: nsNetworkPolicyProvider,
|
||||
// namespacenpController implements the Controller interface for managing kubesphere network policies
|
||||
// and convery them to k8s NetworkPolicies, then syncing them to the provider.
|
||||
type NSNetworkPolicyController struct {
|
||||
client kubernetes.Interface
|
||||
ksclient ksnetclient.NetworkV1alpha1Interface
|
||||
informer nspolicy.NamespaceNetworkPolicyInformer
|
||||
informerSynced cache.InformerSynced
|
||||
|
||||
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "NamespaceNetworkPolicies"),
|
||||
recorder: recorder,
|
||||
}
|
||||
log.Info("Setting up event handlers")
|
||||
nsnpInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: ctl.enqueueNSNP,
|
||||
UpdateFunc: func(old, new interface{}) {
|
||||
ctl.enqueueNSNP(new)
|
||||
},
|
||||
DeleteFunc: ctl.enqueueNSNP,
|
||||
})
|
||||
return ctl
|
||||
serviceInformer v1.ServiceInformer
|
||||
serviceInformerSynced cache.InformerSynced
|
||||
|
||||
workspaceInformer workspace.WorkspaceInformer
|
||||
workspaceInformerSynced cache.InformerSynced
|
||||
|
||||
namespaceInformer v1.NamespaceInformer
|
||||
namespaceInformerSynced cache.InformerSynced
|
||||
|
||||
provider provider.NsNetworkPolicyProvider
|
||||
|
||||
nsQueue workqueue.RateLimitingInterface
|
||||
nsnpQueue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
func (c *controller) Run(threadiness int, stopCh <-chan struct{}) error {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.workqueue.ShutDown()
|
||||
|
||||
//init client
|
||||
|
||||
// Start the informer factories to begin populating the informer caches
|
||||
log.V(1).Info("Starting WSNP controller")
|
||||
|
||||
// Wait for the caches to be synced before starting workers
|
||||
log.V(2).Info("Waiting for informer caches to sync")
|
||||
if ok := cache.WaitForCacheSync(stopCh, c.nsnpSynced); !ok {
|
||||
return fmt.Errorf("failed to wait for caches to sync")
|
||||
func (c *NSNetworkPolicyController) convertPeer(peers []v1alpha1.NetworkPolicyPeer) ([]netv1.NetworkPolicyPeer, error) {
|
||||
if len(peers) <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Info("Starting workers")
|
||||
// Launch two workers to process Foo resources
|
||||
for i := 0; i < threadiness; i++ {
|
||||
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||
rules := make([]netv1.NetworkPolicyPeer, 0)
|
||||
|
||||
for _, peer := range peers {
|
||||
rule := netv1.NetworkPolicyPeer{}
|
||||
|
||||
if peer.ServiceSelector != nil {
|
||||
rule.PodSelector = new(metav1.LabelSelector)
|
||||
rule.NamespaceSelector = new(metav1.LabelSelector)
|
||||
|
||||
namespace := peer.ServiceSelector.Namespace
|
||||
name := peer.ServiceSelector.Name
|
||||
service, err := c.serviceInformer.Lister().Services(namespace).Get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(service.Spec.Selector) <= 0 {
|
||||
return nil, fmt.Errorf("service %s/%s has no podselect", namespace, name)
|
||||
}
|
||||
|
||||
rule.PodSelector.MatchLabels = make(map[string]string)
|
||||
for key, value := range service.Spec.Selector {
|
||||
rule.PodSelector.MatchLabels[key] = value
|
||||
}
|
||||
rule.NamespaceSelector.MatchLabels = make(map[string]string)
|
||||
rule.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
|
||||
} else if peer.NamespaceSelector != nil {
|
||||
name := peer.NamespaceSelector.Name
|
||||
|
||||
rule.NamespaceSelector = new(metav1.LabelSelector)
|
||||
rule.NamespaceSelector.MatchLabels = make(map[string]string)
|
||||
rule.NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = name
|
||||
} else if peer.IPBlock != nil {
|
||||
rule.IPBlock = peer.IPBlock
|
||||
} else {
|
||||
klog.Errorf("Invalid nsnp peer %v\n", peer)
|
||||
continue
|
||||
}
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) convertToK8sNP(n *v1alpha1.NamespaceNetworkPolicy) (*netv1.NetworkPolicy, error) {
|
||||
np := &netv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: n.Name,
|
||||
Namespace: n.Namespace,
|
||||
},
|
||||
Spec: netv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
PolicyTypes: make([]netv1.PolicyType, 0),
|
||||
},
|
||||
}
|
||||
|
||||
if n.Spec.Egress != nil {
|
||||
np.Spec.Egress = make([]netv1.NetworkPolicyEgressRule, len(n.Spec.Egress))
|
||||
for indexEgress, egress := range n.Spec.Egress {
|
||||
rules, err := c.convertPeer(egress.To)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
np.Spec.Egress[indexEgress].To = rules
|
||||
np.Spec.Egress[indexEgress].Ports = egress.Ports
|
||||
}
|
||||
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeEgress)
|
||||
}
|
||||
|
||||
if n.Spec.Ingress != nil {
|
||||
np.Spec.Ingress = make([]netv1.NetworkPolicyIngressRule, len(n.Spec.Ingress))
|
||||
for indexIngress, ingress := range n.Spec.Ingress {
|
||||
rules, err := c.convertPeer(ingress.From)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
np.Spec.Ingress[indexIngress].From = rules
|
||||
np.Spec.Ingress[indexIngress].Ports = ingress.Ports
|
||||
}
|
||||
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, netv1.PolicyTypeIngress)
|
||||
}
|
||||
|
||||
return np, nil
|
||||
}
|
||||
|
||||
func generateNSNP(workspace string, namespace string, matchWorkspace bool) *netv1.NetworkPolicy {
|
||||
policy := &netv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: AnnotationNPNAME,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: netv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{},
|
||||
PolicyTypes: make([]netv1.PolicyType, 0),
|
||||
Ingress: []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
Egress: []netv1.NetworkPolicyEgressRule{{
|
||||
To: []netv1.NetworkPolicyPeer{{
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
policy.Spec.PolicyTypes = append(policy.Spec.PolicyTypes, netv1.PolicyTypeIngress, netv1.PolicyTypeEgress)
|
||||
|
||||
if matchWorkspace {
|
||||
policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace
|
||||
policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.WorkspaceLabelKey] = workspace
|
||||
} else {
|
||||
policy.Spec.Ingress[0].From[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
|
||||
policy.Spec.Egress[0].To[0].NamespaceSelector.MatchLabels[constants.NamespaceLabelKey] = namespace
|
||||
}
|
||||
|
||||
return policy
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) nsEnqueue(ns *corev1.Namespace) {
|
||||
key, err := cache.MetaNamespaceKeyFunc(ns)
|
||||
if err != nil {
|
||||
uruntime.HandleError(fmt.Errorf("Get namespace key %s failed", ns.Name))
|
||||
return
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Enqueue namespace %s", ns.Name)
|
||||
c.nsQueue.Add(key)
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) addWorkspace(newObj interface{}) {
|
||||
new := newObj.(*workspacev1alpha1.Workspace)
|
||||
|
||||
klog.V(4).Infof("Add workspace %s", new.Name)
|
||||
|
||||
label := labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: new.Name})
|
||||
nsList, err := c.namespaceInformer.Lister().List(label)
|
||||
if err != nil {
|
||||
klog.Errorf("Error while list namespace by label %s", label.String())
|
||||
return
|
||||
}
|
||||
|
||||
for _, ns := range nsList {
|
||||
c.nsEnqueue(ns)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) addNamespace(obj interface{}) {
|
||||
ns := obj.(*corev1.Namespace)
|
||||
|
||||
workspaceName := ns.Labels[constants.WorkspaceLabelKey]
|
||||
if workspaceName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Add namespace %s", ns.Name)
|
||||
|
||||
c.nsEnqueue(ns)
|
||||
}
|
||||
|
||||
func isNetworkIsolateEnabled(ns *corev1.Namespace) bool {
|
||||
if ns.Annotations[NamespaceNPAnnotationKey] == NamespaceNPAnnotationEnabled {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func hadNamespaceLabel(ns *corev1.Namespace) bool {
|
||||
if ns.Annotations[constants.NamespaceLabelKey] == ns.Name {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) syncNs(key string) error {
|
||||
klog.V(4).Infof("Sync namespace %s", key)
|
||||
|
||||
_, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
klog.Errorf("Not a valid controller key %s, %#v", key, err)
|
||||
return err
|
||||
}
|
||||
|
||||
ns, err := c.namespaceInformer.Lister().Get(name)
|
||||
if err != nil {
|
||||
// not found, possibly been deleted
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(2).Infof("Namespace %v has been deleted", key)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
workspaceName := ns.Labels[constants.WorkspaceLabelKey]
|
||||
if workspaceName == "" {
|
||||
klog.Error("Workspace name should not be empty")
|
||||
return nil
|
||||
}
|
||||
wksp, err := c.workspaceInformer.Lister().Get(workspaceName)
|
||||
if err != nil {
|
||||
//Should not be here
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(2).Infof("Workspace %v has been deleted", workspaceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Maybe some ns not labeled
|
||||
if !hadNamespaceLabel(ns) {
|
||||
ns.Labels[constants.NamespaceLabelKey] = ns.Name
|
||||
_, err := c.client.CoreV1().Namespaces().Update(ns)
|
||||
if err != nil {
|
||||
//Just log, label can also be added by namespace controller
|
||||
klog.Errorf("cannot label namespace %s", ns.Name)
|
||||
}
|
||||
}
|
||||
|
||||
matchWorkspace := false
|
||||
delete := false
|
||||
if isNetworkIsolateEnabled(ns) {
|
||||
matchWorkspace = false
|
||||
} else if wksp.Spec.NetworkIsolation {
|
||||
matchWorkspace = true
|
||||
} else {
|
||||
delete = true
|
||||
}
|
||||
|
||||
policy := generateNSNP(workspaceName, ns.Name, matchWorkspace)
|
||||
if delete {
|
||||
c.provider.Delete(c.provider.GetKey(AnnotationNPNAME, ns.Name))
|
||||
//delete all namespace np when networkisolate not active
|
||||
if c.ksclient.NamespaceNetworkPolicies(ns.Name).DeleteCollection(nil, typev1.ListOptions{}) != nil {
|
||||
klog.Errorf("Error when delete all nsnps in namespace %s", ns.Name)
|
||||
}
|
||||
} else {
|
||||
err = c.provider.Set(policy)
|
||||
if err != nil {
|
||||
klog.Errorf("Error while converting %#v to provider's network policy.", policy)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
klog.V(2).Info("Started workers")
|
||||
<-stopCh
|
||||
log.V(2).Info("Shutting down workers")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) enqueueNSNP(obj interface{}) {
|
||||
var key string
|
||||
var err error
|
||||
if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil {
|
||||
utilruntime.HandleError(err)
|
||||
return
|
||||
}
|
||||
c.workqueue.Add(key)
|
||||
}
|
||||
|
||||
func (c *controller) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
func (c *NSNetworkPolicyController) nsWorker() {
|
||||
for c.processNsWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) processNextWorkItem() bool {
|
||||
obj, shutdown := c.workqueue.Get()
|
||||
|
||||
if shutdown {
|
||||
func (c *NSNetworkPolicyController) processNsWorkItem() bool {
|
||||
key, quit := c.nsQueue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.nsQueue.Done(key)
|
||||
|
||||
// We wrap this block in a func so we can defer c.workqueue.Done.
|
||||
err := func(obj interface{}) error {
|
||||
// We call Done here so the workqueue knows we have finished
|
||||
// processing this item. We also must remember to call Forget if we
|
||||
// do not want this work item being re-queued. For example, we do
|
||||
// not call Forget if a transient error occurs, instead the item is
|
||||
// put back on the workqueue and attempted again after a back-off
|
||||
// period.
|
||||
defer c.workqueue.Done(obj)
|
||||
var key string
|
||||
var ok bool
|
||||
// We expect strings to come off the workqueue. These are of the
|
||||
// form namespace/name. We do this as the delayed nature of the
|
||||
// workqueue means the items in the informer cache may actually be
|
||||
// more up to date that when the item was initially put onto the
|
||||
// workqueue.
|
||||
if key, ok = obj.(string); !ok {
|
||||
// As the item in the workqueue is actually invalid, we call
|
||||
// Forget here else we'd go into a loop of attempting to
|
||||
// process a work item that is invalid.
|
||||
c.workqueue.Forget(obj)
|
||||
utilruntime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj))
|
||||
return nil
|
||||
}
|
||||
// Run the reconcile, passing it the namespace/name string of the
|
||||
// Foo resource to be synced.
|
||||
if err := c.reconcile(key); err != nil {
|
||||
// Put the item back on the workqueue to handle any transient errors.
|
||||
c.workqueue.AddRateLimited(key)
|
||||
return fmt.Errorf("error syncing '%s': %s, requeuing", key, err.Error())
|
||||
}
|
||||
// Finally, if no error occurs we Forget this item so it does not
|
||||
// get queued again until another change happens.
|
||||
c.workqueue.Forget(obj)
|
||||
log.Info("Successfully synced", "key", key)
|
||||
return nil
|
||||
}(obj)
|
||||
|
||||
if err != nil {
|
||||
utilruntime.HandleError(err)
|
||||
return true
|
||||
if err := c.syncNs(key.(string)); err != nil {
|
||||
klog.Errorf("Error when syncns %s", err)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) nsnpEnqueue(obj interface{}) {
|
||||
nsnp := obj.(*v1alpha1.NamespaceNetworkPolicy)
|
||||
|
||||
key, err := cache.MetaNamespaceKeyFunc(nsnp)
|
||||
if err != nil {
|
||||
uruntime.HandleError(fmt.Errorf("get namespace network policy key %s failed", nsnp.Name))
|
||||
return
|
||||
}
|
||||
|
||||
c.nsnpQueue.Add(key)
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) syncNSNP(key string) error {
|
||||
namespace, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
klog.Errorf("Not a valid controller key %s, %#v", key, err)
|
||||
return err
|
||||
}
|
||||
|
||||
ns, err := c.namespaceInformer.Lister().Get(namespace)
|
||||
if !isNetworkIsolateEnabled(ns) {
|
||||
klog.Infof("Delete NSNP %s when namespace isolate is inactive", key)
|
||||
c.provider.Delete(c.provider.GetKey(name, namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
nsnp, err := c.informer.Lister().NamespaceNetworkPolicies(namespace).Get(name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(4).Infof("NSNP %v has been deleted", key)
|
||||
c.provider.Delete(c.provider.GetKey(name, namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
np, err := c.convertToK8sNP(nsnp)
|
||||
if err != nil {
|
||||
klog.Errorf("Error while convert nsnp to k8snp: %s", err)
|
||||
return err
|
||||
}
|
||||
err = c.provider.Set(np)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) nsNPWorker() {
|
||||
for c.processNSNPWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) processNSNPWorkItem() bool {
|
||||
key, quit := c.nsnpQueue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.nsnpQueue.Done(key)
|
||||
|
||||
c.syncNSNP(key.(string))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// NewnamespacenpController returns a controller which manages NSNSP objects.
|
||||
func NewNSNetworkPolicyController(
|
||||
client kubernetes.Interface,
|
||||
ksclient ksnetclient.NetworkV1alpha1Interface,
|
||||
nsnpInformer nspolicy.NamespaceNetworkPolicyInformer,
|
||||
serviceInformer v1.ServiceInformer,
|
||||
workspaceInformer workspace.WorkspaceInformer,
|
||||
namespaceInformer v1.NamespaceInformer,
|
||||
policyProvider provider.NsNetworkPolicyProvider) *NSNetworkPolicyController {
|
||||
|
||||
controller := &NSNetworkPolicyController{
|
||||
client: client,
|
||||
ksclient: ksclient,
|
||||
informer: nsnpInformer,
|
||||
informerSynced: nsnpInformer.Informer().HasSynced,
|
||||
serviceInformer: serviceInformer,
|
||||
serviceInformerSynced: serviceInformer.Informer().HasSynced,
|
||||
workspaceInformer: workspaceInformer,
|
||||
workspaceInformerSynced: workspaceInformer.Informer().HasSynced,
|
||||
namespaceInformer: namespaceInformer,
|
||||
namespaceInformerSynced: namespaceInformer.Informer().HasSynced,
|
||||
provider: policyProvider,
|
||||
nsQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespace"),
|
||||
nsnpQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "namespacenp"),
|
||||
}
|
||||
|
||||
workspaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.addWorkspace,
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
old := oldObj.(*workspacev1alpha1.Workspace)
|
||||
new := oldObj.(*workspacev1alpha1.Workspace)
|
||||
if old.Spec.NetworkIsolation == new.Spec.NetworkIsolation {
|
||||
return
|
||||
}
|
||||
controller.addWorkspace(newObj)
|
||||
},
|
||||
})
|
||||
|
||||
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: controller.addNamespace,
|
||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||
old := oldObj.(*corev1.Namespace)
|
||||
new := oldObj.(*corev1.Namespace)
|
||||
if old.Annotations[NamespaceNPAnnotationKey] == new.Annotations[NamespaceNPAnnotationKey] {
|
||||
return
|
||||
}
|
||||
controller.addNamespace(newObj)
|
||||
},
|
||||
})
|
||||
|
||||
nsnpInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
klog.V(4).Infof("Got ADD event for NSNSP: %#v", obj)
|
||||
controller.nsnpEnqueue(obj)
|
||||
},
|
||||
UpdateFunc: func(oldObj interface{}, newObj interface{}) {
|
||||
klog.V(4).Info("Got UPDATE event for NSNSP.")
|
||||
klog.V(4).Infof("Old object: \n%#v\n", oldObj)
|
||||
klog.V(4).Infof("New object: \n%#v\n", newObj)
|
||||
controller.nsnpEnqueue(newObj)
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
klog.V(4).Infof("Got DELETE event for NSNP: %#v", obj)
|
||||
controller.nsnpEnqueue(obj)
|
||||
},
|
||||
}, defaultSleepDuration)
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
func (c *NSNetworkPolicyController) Start(stopCh <-chan struct{}) error {
|
||||
return c.Run(defaultThread, defaultSync, stopCh)
|
||||
}
|
||||
|
||||
// Run starts the controller.
|
||||
func (c *NSNetworkPolicyController) Run(threadiness int, reconcilerPeriod string, stopCh <-chan struct{}) error {
|
||||
defer uruntime.HandleCrash()
|
||||
|
||||
defer c.nsQueue.ShutDown()
|
||||
defer c.nsnpQueue.ShutDown()
|
||||
|
||||
klog.Info("Waiting to sync with Kubernetes API (NSNP)")
|
||||
if ok := cache.WaitForCacheSync(stopCh, c.informerSynced, c.serviceInformerSynced, c.workspaceInformerSynced, c.namespaceInformerSynced); !ok {
|
||||
return fmt.Errorf("Failed to wait for caches to sync")
|
||||
}
|
||||
klog.Info("Finished syncing with Kubernetes API (NSNP)")
|
||||
|
||||
// Start a number of worker threads to read from the queue. Each worker
|
||||
// will pull keys off the resource cache event queue and sync them to the
|
||||
// K8s datastore.
|
||||
for i := 0; i < threadiness; i++ {
|
||||
go wait.Until(c.nsWorker, time.Second, stopCh)
|
||||
go wait.Until(c.nsNPWorker, time.Second, stopCh)
|
||||
}
|
||||
|
||||
//Work to sync K8s NetworkPolicy
|
||||
go c.provider.Start(stopCh)
|
||||
|
||||
klog.Info("NSNP controller is now running")
|
||||
<-stopCh
|
||||
klog.Info("Stopping NSNP controller")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
|
||||
func TestNsnetworkpolicy(t *testing.T) {
|
||||
klog.InitFlags(nil)
|
||||
flag.Set("logtostderr", "false")
|
||||
flag.Set("alsologtostderr", "false")
|
||||
flag.Set("logtostderr", "true")
|
||||
flag.Set("v", "4")
|
||||
flag.Parse()
|
||||
klog.SetOutput(GinkgoWriter)
|
||||
|
||||
@@ -1,90 +1,285 @@
|
||||
package nsnetworkpolicy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
nsnplister "kubesphere.io/kubesphere/pkg/client/listers/network/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/controller/network/controllerapi"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
netv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
informerv1 "k8s.io/client-go/informers/core/v1"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
netv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
wkspv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
|
||||
ksfake "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
||||
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
nsnppolicyinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
|
||||
workspaceinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/controller/network/provider"
|
||||
controllertesting "kubesphere.io/kubesphere/pkg/controller/network/testing"
|
||||
)
|
||||
|
||||
var (
|
||||
fakeControllerBuilder *controllertesting.FakeControllerBuilder
|
||||
c controllerapi.Controller
|
||||
stopCh chan struct{}
|
||||
calicoProvider *provider.FakeCalicoNetworkProvider
|
||||
nsnpLister nsnplister.NamespaceNetworkPolicyLister
|
||||
c *NSNetworkPolicyController
|
||||
stopCh chan struct{}
|
||||
nsnpInformer nsnppolicyinformer.NamespaceNetworkPolicyInformer
|
||||
serviceInformer informerv1.ServiceInformer
|
||||
workspaceInformer workspaceinformer.WorkspaceInformer
|
||||
namespaceInformer informerv1.NamespaceInformer
|
||||
alwaysReady = func() bool { return true }
|
||||
)
|
||||
|
||||
const (
|
||||
workspaceNP = `
|
||||
apiVersion: "networking.k8s.io/v1"
|
||||
kind: "NetworkPolicy"
|
||||
metadata:
|
||||
name: networkisolate
|
||||
namespace: %s
|
||||
spec:
|
||||
podSelector: {}
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
%s: %s
|
||||
Egress:
|
||||
- To:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
%s: %s
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress`
|
||||
|
||||
serviceTmp = `
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: myservice
|
||||
namespace: testns
|
||||
spec:
|
||||
selector:
|
||||
app: mylbapp
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 9376
|
||||
`
|
||||
|
||||
workspaceTmp = `
|
||||
apiVersion: tenant.kubesphere.io/v1alpha1
|
||||
kind: Workspace
|
||||
metadata:
|
||||
annotations:
|
||||
kubesphere.io/creator: admin
|
||||
name: testworkspace
|
||||
spec:
|
||||
manager: admin
|
||||
networkIsolation: true
|
||||
status: {}
|
||||
`
|
||||
|
||||
nsTmp = `
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
kubesphere.io/workspace: testworkspace
|
||||
name: testns
|
||||
spec:
|
||||
finalizers:
|
||||
- kubernetes
|
||||
`
|
||||
)
|
||||
|
||||
func StringToObject(data string, obj interface{}) error {
|
||||
reader := strings.NewReader(data)
|
||||
return yaml.NewYAMLOrJSONDecoder(reader, 10).Decode(obj)
|
||||
}
|
||||
|
||||
var _ = Describe("Nsnetworkpolicy", func() {
|
||||
BeforeEach(func() {
|
||||
fakeControllerBuilder = controllertesting.NewFakeControllerBuilder()
|
||||
stopCh = make(chan struct{})
|
||||
informer, _ := fakeControllerBuilder.NewControllerInformer()
|
||||
calicoProvider = provider.NewFakeCalicoNetworkProvider()
|
||||
c = NewController(fakeControllerBuilder.KubeClient, fakeControllerBuilder.KsClient, informer.Network().V1alpha1().NamespaceNetworkPolicies(), calicoProvider)
|
||||
go informer.Network().V1alpha1().NamespaceNetworkPolicies().Informer().Run(stopCh)
|
||||
originalController := c.(*controller)
|
||||
originalController.recorder = &record.FakeRecorder{}
|
||||
go c.Run(1, stopCh)
|
||||
nsnpLister = informer.Network().V1alpha1().NamespaceNetworkPolicies().Lister()
|
||||
calicoProvider := provider.NewFakeNetworkProvider()
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset()
|
||||
ksClient := ksfake.NewSimpleClientset()
|
||||
kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, 0)
|
||||
ksInformer := ksinformers.NewSharedInformerFactory(ksClient, 0)
|
||||
|
||||
nsnpInformer := ksInformer.Network().V1alpha1().NamespaceNetworkPolicies()
|
||||
serviceInformer := kubeInformer.Core().V1().Services()
|
||||
workspaceInformer := ksInformer.Tenant().V1alpha1().Workspaces()
|
||||
namespaceInformer := kubeInformer.Core().V1().Namespaces()
|
||||
|
||||
c = NewNSNetworkPolicyController(kubeClient, ksClient.NetworkV1alpha1(), nsnpInformer, serviceInformer, workspaceInformer, namespaceInformer, calicoProvider)
|
||||
|
||||
serviceObj := &corev1.Service{}
|
||||
Expect(StringToObject(serviceTmp, serviceObj)).ShouldNot(HaveOccurred())
|
||||
Expect(serviceInformer.Informer().GetIndexer().Add(serviceObj)).ShouldNot(HaveOccurred())
|
||||
nsObj := &corev1.Namespace{}
|
||||
Expect(StringToObject(nsTmp, nsObj)).ShouldNot(HaveOccurred())
|
||||
namespaceInformer.Informer().GetIndexer().Add(nsObj)
|
||||
workspaceObj := &wkspv1alpha1.Workspace{}
|
||||
Expect(StringToObject(workspaceTmp, workspaceObj)).ShouldNot(HaveOccurred())
|
||||
workspaceInformer.Informer().GetIndexer().Add(workspaceObj)
|
||||
|
||||
c.namespaceInformerSynced = alwaysReady
|
||||
c.serviceInformerSynced = alwaysReady
|
||||
c.workspaceInformerSynced = alwaysReady
|
||||
c.informerSynced = alwaysReady
|
||||
|
||||
go c.Start(stopCh)
|
||||
})
|
||||
|
||||
It("Should create a new calico object", func() {
|
||||
objSrt := `{
|
||||
"apiVersion": "network.kubesphere.io/v1alpha1",
|
||||
"kind": "NetworkPolicy",
|
||||
"metadata": {
|
||||
"name": "allow-tcp-6379",
|
||||
"namespace": "production"
|
||||
},
|
||||
"spec": {
|
||||
"selector": "color == 'red'",
|
||||
"ingress": [
|
||||
{
|
||||
"action": "Allow",
|
||||
"protocol": "TCP",
|
||||
"source": {
|
||||
"selector": "color == 'blue'"
|
||||
},
|
||||
"destination": {
|
||||
"ports": [
|
||||
6379
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
obj := &v1alpha1.NamespaceNetworkPolicy{}
|
||||
Expect(controllertesting.StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
_, err := fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Create(obj)
|
||||
It("Should create ns networkisolate np correctly in workspace", func() {
|
||||
objSrt := fmt.Sprintf(workspaceNP, "testns", constants.WorkspaceLabelKey, "testworkspace", constants.WorkspaceLabelKey, "testworkspace")
|
||||
obj := &netv1.NetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
|
||||
policy := generateNSNP("testworkspace", "testns", true)
|
||||
Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Should create ns networkisolate np correctly in ns", func() {
|
||||
objSrt := fmt.Sprintf(workspaceNP, "testns", constants.NamespaceLabelKey, "testns", constants.NamespaceLabelKey, "testns")
|
||||
obj := &netv1.NetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
|
||||
policy := generateNSNP("testworkspace", "testns", false)
|
||||
Expect(reflect.DeepEqual(obj.Spec, policy.Spec)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("test func convertToK8sNP", func() {
|
||||
objSrt := `
|
||||
apiVersion: network.kubesphere.io/v1alpha1
|
||||
kind: NamespaceNetworkPolicy
|
||||
metadata:
|
||||
name: namespaceIPblockNP
|
||||
namespace: testns
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 172.0.0.1/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
`
|
||||
obj := &netv1alpha1.NamespaceNetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
policy, err := c.convertToK8sNP(obj)
|
||||
|
||||
objSrt = `
|
||||
apiVersion: "networking.k8s.io/v1"
|
||||
kind: "NetworkPolicy"
|
||||
metadata:
|
||||
name: IPblockNP
|
||||
namespace: testns
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- ipBlock:
|
||||
cidr: 172.0.0.1/16
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
policyTypes:
|
||||
- Ingress
|
||||
`
|
||||
obj2 := &netv1.NetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Eventually(func() bool {
|
||||
exist, _ := calicoProvider.CheckExist(obj)
|
||||
return exist
|
||||
}).Should(BeTrue())
|
||||
obj, _ = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Get(obj.Name, metav1.GetOptions{})
|
||||
Expect(obj.Finalizers).To(HaveLen(1))
|
||||
// TestUpdate
|
||||
newStr := "color == 'green'"
|
||||
obj.Spec.Selector = newStr
|
||||
_, err = fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
|
||||
Expect(reflect.DeepEqual(obj2.Spec, policy.Spec)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("test func convertToK8sNP with namespace", func() {
|
||||
objSrt := `
|
||||
apiVersion: network.kubesphere.io/v1alpha1
|
||||
kind: NamespaceNetworkPolicy
|
||||
metadata:
|
||||
name: testnamespace
|
||||
namespace: testns2
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- namespace:
|
||||
name: testns
|
||||
`
|
||||
obj := &netv1alpha1.NamespaceNetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
|
||||
np, err := c.convertToK8sNP(obj)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Eventually(func() string {
|
||||
o, err := calicoProvider.Get(obj)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
n := o.(*v1alpha1.NamespaceNetworkPolicy)
|
||||
return n.Spec.Selector
|
||||
}).Should(Equal(newStr))
|
||||
// TestDelete
|
||||
Expect(fakeControllerBuilder.KsClient.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Delete(obj.Name, &metav1.DeleteOptions{})).ShouldNot(HaveOccurred())
|
||||
|
||||
objTmp := `
|
||||
apiVersion: "networking.k8s.io/v1"
|
||||
kind: "NetworkPolicy"
|
||||
metadata:
|
||||
name: testnamespace
|
||||
namespace: testns2
|
||||
spec:
|
||||
podSelector: {}
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
%s: %s
|
||||
policyTypes:
|
||||
- Ingress`
|
||||
objSrt = fmt.Sprintf(objTmp, constants.NamespaceLabelKey, "testns")
|
||||
obj2 := &netv1.NetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
|
||||
Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("test func convertToK8sNP with service", func() {
|
||||
objSrt := `
|
||||
apiVersion: network.kubesphere.io/v1alpha1
|
||||
kind: NamespaceNetworkPolicy
|
||||
metadata:
|
||||
name: testnamespace
|
||||
namespace: testns2
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- service:
|
||||
name: myservice
|
||||
namespace: testns
|
||||
`
|
||||
obj := &netv1alpha1.NamespaceNetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj)).ShouldNot(HaveOccurred())
|
||||
|
||||
np, err := c.convertToK8sNP(obj)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
objSrt = `
|
||||
apiVersion: "networking.k8s.io/v1"
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: networkisolate
|
||||
namespace: testns
|
||||
spec:
|
||||
podSelector: {}
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: mylbapp
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
kubesphere.io/namespace: testns
|
||||
policyTypes:
|
||||
- Ingress
|
||||
`
|
||||
obj2 := &netv1.NetworkPolicy{}
|
||||
Expect(StringToObject(objSrt, obj2)).ShouldNot(HaveOccurred())
|
||||
Expect(reflect.DeepEqual(np.Spec, obj2.Spec)).To(BeTrue())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package nsnetworkpolicy
|
||||
|
||||
import (
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/retry"
|
||||
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/controller/network/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
controllerFinalizier = "nsnp.finalizers.networking.kubesphere.io"
|
||||
)
|
||||
|
||||
var clog logr.Logger
|
||||
|
||||
func (c *controller) reconcile(key string) error {
|
||||
namespace, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clog = log.WithValues("name", name, "namespace", namespace)
|
||||
clog.V(1).Info("---------Begin to reconcile--------")
|
||||
defer clog.V(1).Info("---------Reconcile done--------")
|
||||
obj, err := c.nsnpLister.NamespaceNetworkPolicies(namespace).Get(name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
clog.V(2).Info("Object is removed")
|
||||
return nil
|
||||
}
|
||||
clog.Error(err, "Failed to get resource")
|
||||
return err
|
||||
}
|
||||
stop, err := c.addOrRemoveFinalizer(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if stop {
|
||||
return nil
|
||||
}
|
||||
clog.V(2).Info("Check if we need a create or update")
|
||||
ok, err := c.nsNetworkPolicyProvider.CheckExist(obj)
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to check exist of network policy")
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
clog.V(1).Info("Create a new object in backend")
|
||||
err = c.nsNetworkPolicyProvider.Add(obj)
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to create np")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
needUpdate, err := c.nsNetworkPolicyProvider.NeedUpdate(obj)
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to check if object need a update")
|
||||
return err
|
||||
}
|
||||
if needUpdate {
|
||||
clog.V(1).Info("Update object in backend")
|
||||
err = c.nsNetworkPolicyProvider.Update(obj)
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to update object")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) addOrRemoveFinalizer(obj *v1alpha1.NamespaceNetworkPolicy) (bool, error) {
|
||||
if obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
if !utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
|
||||
clog.V(2).Info("Detect no finalizer")
|
||||
obj.ObjectMeta.Finalizers = append(obj.ObjectMeta.Finalizers, controllerFinalizier)
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to add finalizer")
|
||||
return false, err
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
} else {
|
||||
// The object is being deleted
|
||||
if utils.ContainsString(obj.ObjectMeta.Finalizers, controllerFinalizier) {
|
||||
// our finalizer is present, so lets handle any external dependency
|
||||
if err := c.deleteProviderNSNP(obj); err != nil {
|
||||
// if fail to delete the external dependency here, return with error
|
||||
// so that it can be retried
|
||||
return false, err
|
||||
}
|
||||
clog.V(2).Info("Removing finalizer")
|
||||
// remove our finalizer from the list and update it.
|
||||
obj.ObjectMeta.Finalizers = utils.RemoveString(obj.ObjectMeta.Finalizers, controllerFinalizier)
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
_, err := c.kubesphereClientset.NetworkV1alpha1().NamespaceNetworkPolicies(obj.Namespace).Update(obj)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
clog.Error(err, "Failed to remove finalizer")
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// deleteProviderNSNP delete network policy in the backend
|
||||
func (c *controller) deleteProviderNSNP(obj *v1alpha1.NamespaceNetworkPolicy) error {
|
||||
clog.V(2).Info("Deleting backend network policy")
|
||||
return c.nsNetworkPolicyProvider.Delete(obj)
|
||||
}
|
||||
38
pkg/controller/network/nsnetworkpolicy/webhook.go
Normal file
38
pkg/controller/network/nsnetworkpolicy/webhook.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package nsnetworkpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
)
|
||||
|
||||
// +kubebuilder:webhook:path=/validate-service-nsnp-kubesphere-io-v1alpha1-network,name=validate-v1-service,mutating=false,failurePolicy=fail,groups="",resources=services,verbs=create;update,versions=v1
|
||||
|
||||
// serviceValidator validates service
|
||||
type ServiceValidator struct {
|
||||
decoder *admission.Decoder
|
||||
}
|
||||
|
||||
// Service must hash label, becasue nsnp will use it
|
||||
func (v *ServiceValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
service := &corev1.Service{}
|
||||
|
||||
err := v.decoder.Decode(req, service)
|
||||
if err != nil {
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
if service.Spec.Selector == nil {
|
||||
return admission.Denied(fmt.Sprintf("missing label"))
|
||||
}
|
||||
|
||||
return admission.Allowed("")
|
||||
}
|
||||
|
||||
func (a *ServiceValidator) InjectDecoder(d *admission.Decoder) error {
|
||||
a.decoder = d
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user