add ippool resource api

add ippool webhook and fix some bugs

Signed-off-by: Duan Jiong <djduanjiong@gmail.com>
This commit is contained in:
Duan Jiong
2020-12-02 18:34:13 +08:00
parent 8a6ce2d7ac
commit 24e3ac865f
30 changed files with 3057 additions and 390 deletions

View File

@@ -22,10 +22,12 @@ import (
"time"
cnet "github.com/projectcalico/libcalico-go/lib/net"
podv1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
@@ -39,6 +41,7 @@ import (
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
networkInformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/controller/network/utils"
"kubesphere.io/kubesphere/pkg/controller/network/webhooks"
"kubesphere.io/kubesphere/pkg/simple/client/network/ippool"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
@@ -62,8 +65,6 @@ type IPPoolController struct {
client clientset.Interface
kubesphereClient kubesphereclient.Interface
options ippool.Options
}
func (c *IPPoolController) ippoolHandle(obj interface{}) {
@@ -112,35 +113,86 @@ func (c *IPPoolController) removeFinalizer(pool *networkv1alpha1.IPPool) error {
return nil
}
// check cidr overlap
func (c *IPPoolController) checkIPPool(pool *networkv1alpha1.IPPool) (bool, error) {
_, poolCIDR, err := cnet.ParseCIDR(pool.Spec.CIDR)
func (c *IPPoolController) ValidateCreate(obj runtime.Object) error {
b := obj.(*networkv1alpha1.IPPool)
_, cidr, err := cnet.ParseCIDR(b.Spec.CIDR)
if err != nil {
return false, err
return fmt.Errorf("invalid cidr")
}
size, _ := cidr.Mask.Size()
if b.Spec.BlockSize > 0 && b.Spec.BlockSize < size {
return fmt.Errorf("the blocksize should be larger than the cidr mask")
}
if b.Spec.RangeStart != "" || b.Spec.RangeEnd != "" {
iStart := cnet.ParseIP(b.Spec.RangeStart)
iEnd := cnet.ParseIP(b.Spec.RangeEnd)
if iStart == nil || iEnd == nil {
return fmt.Errorf("invalid rangeStart or rangeEnd")
}
offsetStart, err := b.IPToOrdinal(*iStart)
if err != nil {
return err
}
offsetEnd, err := b.IPToOrdinal(*iEnd)
if err != nil {
return err
}
if offsetEnd < offsetStart {
return fmt.Errorf("rangeStart should not big than rangeEnd")
}
}
pools, err := c.kubesphereClient.NetworkV1alpha1().IPPools().List(metav1.ListOptions{
LabelSelector: labels.SelectorFromSet(labels.Set{
networkv1alpha1.IPPoolIDLabel: fmt.Sprintf("%d", pool.ID()),
networkv1alpha1.IPPoolIDLabel: fmt.Sprintf("%d", b.ID()),
}).String(),
})
if err != nil {
return false, err
return err
}
for _, p := range pools.Items {
_, cidr, err := cnet.ParseCIDR(p.Spec.CIDR)
if err != nil {
return false, err
}
if cidr.IsNetOverlap(poolCIDR.IPNet) {
return false, ErrCIDROverlap
if b.Overlapped(p) {
return fmt.Errorf("ippool cidr is overlapped with %s", p.Name)
}
}
return true, nil
return nil
}
func (c *IPPoolController) ValidateUpdate(old runtime.Object, new runtime.Object) error {
oldP := old.(*networkv1alpha1.IPPool)
newP := new.(*networkv1alpha1.IPPool)
if newP.Spec.CIDR != oldP.Spec.CIDR {
return fmt.Errorf("cidr cannot be modified")
}
if newP.Spec.Type != oldP.Spec.Type {
return fmt.Errorf("ippool type cannot be modified")
}
if newP.Spec.BlockSize != oldP.Spec.BlockSize {
return fmt.Errorf("ippool blockSize cannot be modified")
}
if newP.Spec.RangeEnd != oldP.Spec.RangeEnd || newP.Spec.RangeStart != oldP.Spec.RangeStart {
return fmt.Errorf("ippool rangeEnd/rangeStart cannot be modified")
}
return nil
}
func (c *IPPoolController) ValidateDelete(obj runtime.Object) error {
p := obj.(*networkv1alpha1.IPPool)
if p.Status.Allocations > 0 {
return fmt.Errorf("ippool is in use, please remove the workload before deleting")
}
return nil
}
func (c *IPPoolController) disableIPPool(old *networkv1alpha1.IPPool) error {
@@ -159,18 +211,19 @@ func (c *IPPoolController) disableIPPool(old *networkv1alpha1.IPPool) error {
func (c *IPPoolController) updateIPPoolStatus(old *networkv1alpha1.IPPool) error {
new, err := c.provider.GetIPPoolStats(old)
if err != nil {
return err
return fmt.Errorf("failed to get ippool %s status %v", old.Name, err)
}
if reflect.DeepEqual(old.Status, new.Status) {
return nil
}
clone := old.DeepCopy()
clone.Status = new.Status
old, err = c.kubesphereClient.NetworkV1alpha1().IPPools().Update(clone)
_, err = c.kubesphereClient.NetworkV1alpha1().IPPools().UpdateStatus(new)
if err != nil {
return fmt.Errorf("failed to update ippool %s status %v", old.Name, err)
}
return err
return nil
}
func (c *IPPoolController) processIPPool(name string) (*time.Duration, error) {
@@ -181,10 +234,16 @@ func (c *IPPoolController) processIPPool(name string) (*time.Duration, error) {
}()
pool, err := c.ippoolInformer.Lister().Get(name)
if apierrors.IsNotFound(err) {
if err != nil {
if apierrors.IsNotFound(err) {
return nil, nil
}
return nil, fmt.Errorf("failed to get ippool %s: %v", name, err)
}
if pool.Type() != c.provider.Type() {
klog.V(4).Infof("pool %s type not match, ignored", pool.Name)
return nil, nil
} else if err != nil {
return nil, err
}
if utils.IsDeletionCandidate(pool, networkv1alpha1.IPPoolFinalizer) {
@@ -199,6 +258,7 @@ func (c *IPPoolController) processIPPool(name string) (*time.Duration, error) {
if err != nil {
return nil, err
}
if canDelete {
return nil, c.removeFinalizer(pool)
}
@@ -209,14 +269,6 @@ func (c *IPPoolController) processIPPool(name string) (*time.Duration, error) {
}
if utils.NeedToAddFinalizer(pool, networkv1alpha1.IPPoolFinalizer) {
valid, err := c.checkIPPool(pool)
if err != nil {
return nil, err
}
if !valid {
return nil, nil
}
err = c.addFinalizer(pool)
if err != nil {
return nil, err
@@ -310,7 +362,6 @@ func NewIPPoolController(
ipamblockInformer networkInformer.IPAMBlockInformer,
client clientset.Interface,
kubesphereClient kubesphereclient.Interface,
options ippool.Options,
provider ippool.Provider) *IPPoolController {
broadcaster := record.NewBroadcaster()
@@ -318,7 +369,7 @@ func NewIPPoolController(
klog.Info(fmt.Sprintf(format, args))
})
broadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: client.CoreV1().Events("")})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "cluster-controller"})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "ippool-controller"})
c := &IPPoolController{
eventBroadcaster: broadcaster,
@@ -330,7 +381,6 @@ func NewIPPoolController(
ipamblockSynced: ipamblockInformer.Informer().HasSynced,
client: client,
kubesphereClient: kubesphereClient,
options: options,
provider: provider,
}
@@ -350,5 +400,11 @@ func NewIPPoolController(
DeleteFunc: c.ipamblockHandle,
})
//register ippool webhook
webhooks.RegisterValidator(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindIPPool).String(),
&webhooks.ValidatorWrap{Obj: &networkv1alpha1.IPPool{}, Helper: c})
webhooks.RegisterDefaulter(podv1.SchemeGroupVersion.WithKind("Pod").String(),
&webhooks.DefaulterWrap{Obj: &podv1.Pod{}, Helper: provider})
return c
}

View File

@@ -18,6 +18,9 @@ package ippool
import (
"flag"
"testing"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -29,7 +32,6 @@ import (
"kubesphere.io/kubesphere/pkg/controller/network/utils"
"kubesphere.io/kubesphere/pkg/simple/client/network/ippool"
"kubesphere.io/kubesphere/pkg/simple/client/network/ippool/ipam"
"testing"
)
func TestIPPoolSuit(t *testing.T) {
@@ -49,31 +51,52 @@ var _ = Describe("test ippool", func() {
Name: "testippool",
},
Spec: v1alpha1.IPPoolSpec{
Type: v1alpha1.VLAN,
CIDR: "192.168.0.0/24",
BlockSize: 24,
Type: v1alpha1.VLAN,
CIDR: "192.168.0.0/24",
},
Status: v1alpha1.IPPoolStatus{},
}
ksclient := ksfake.NewSimpleClientset()
k8sclinet := k8sfake.NewSimpleClientset()
options := ippool.Options{}
p := ippool.NewProvider(ksclient, options)
p := ippool.NewProvider(nil, ksclient, k8sclinet, v1alpha1.IPPoolTypeLocal, nil)
ipamClient := ipam.NewIPAMClient(ksclient, v1alpha1.VLAN)
ksInformer := ksinformers.NewSharedInformerFactory(ksclient, 0)
ippoolInformer := ksInformer.Network().V1alpha1().IPPools()
ipamblockInformer := ksInformer.Network().V1alpha1().IPAMBlocks()
c := NewIPPoolController(ippoolInformer, ipamblockInformer, k8sclinet, ksclient, options, p)
c := NewIPPoolController(ippoolInformer, ipamblockInformer, k8sclinet, ksclient, p)
stopCh := make(chan struct{})
go ksInformer.Start(stopCh)
go c.Start(stopCh)
It("test create ippool", func() {
_, err := ksclient.NetworkV1alpha1().IPPools().Create(pool)
clone := pool.DeepCopy()
clone.Spec.CIDR = "testxxx"
Expect(c.ValidateCreate(clone)).Should(HaveOccurred())
clone = pool.DeepCopy()
clone.Spec.CIDR = "192.168.0.0/24"
clone.Spec.RangeStart = "192.168.0.100"
clone.Spec.RangeEnd = "192.168.0.99"
Expect(c.ValidateCreate(clone)).Should(HaveOccurred())
clone = pool.DeepCopy()
clone.Spec.CIDR = "192.168.0.0/24"
clone.Spec.RangeStart = "192.168.3.100"
clone.Spec.RangeEnd = "192.168.3.111"
Expect(c.ValidateCreate(clone)).Should(HaveOccurred())
clone = pool.DeepCopy()
clone.Spec.CIDR = "192.168.0.0/24"
clone.Spec.BlockSize = 23
Expect(c.ValidateCreate(clone)).Should(HaveOccurred())
clone = pool.DeepCopy()
_, err := ksclient.NetworkV1alpha1().IPPools().Create(clone)
Expect(err).ShouldNot(HaveOccurred())
Eventually(func() bool {
result, _ := ksclient.NetworkV1alpha1().IPPools().Get(pool.Name, v1.GetOptions{})
if len(result.Labels) != 3 {
@@ -85,7 +108,17 @@ var _ = Describe("test ippool", func() {
}
return true
}).Should(Equal(true))
}, 3*time.Second).Should(Equal(true))
clone = pool.DeepCopy()
Expect(c.ValidateCreate(clone)).Should(HaveOccurred())
})
It("test update ippool", func() {
old, _ := ksclient.NetworkV1alpha1().IPPools().Get(pool.Name, v1.GetOptions{})
new := old.DeepCopy()
new.Spec.CIDR = "192.168.1.0/24"
Expect(c.ValidateUpdate(old, new)).Should(HaveOccurred())
})
It("test ippool stats", func() {
@@ -102,10 +135,13 @@ var _ = Describe("test ippool", func() {
}
return true
}).Should(Equal(true))
}, 3*time.Second).Should(Equal(true))
})
It("test delete pool", func() {
result, _ := ksclient.NetworkV1alpha1().IPPools().Get(pool.Name, v1.GetOptions{})
Expect(c.ValidateDelete(result)).Should(HaveOccurred())
ipamClient.ReleaseByHandle("testhandle")
Eventually(func() bool {
result, _ := ksclient.NetworkV1alpha1().IPPools().Get(pool.Name, v1.GetOptions{})
@@ -114,7 +150,7 @@ var _ = Describe("test ippool", func() {
}
return true
}).Should(Equal(true))
}, 3*time.Second).Should(Equal(true))
err := ksclient.NetworkV1alpha1().IPPools().Delete(pool.Name, &v1.DeleteOptions{})
Expect(err).ShouldNot(HaveOccurred())