Merge pull request #3216 from duanjiong/ippool

add ippool resource api
This commit is contained in:
KubeSphere CI Bot
2020-12-29 19:47:52 +08:00
committed by GitHub
31 changed files with 3080 additions and 390 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -51,7 +51,6 @@ import (
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/simple/client/network"
ippoolclient "kubesphere.io/kubesphere/pkg/simple/client/network/ippool"
calicoclient "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"sigs.k8s.io/controller-runtime/pkg/manager"
@@ -261,17 +260,12 @@ func addControllers(
}
var ippoolController manager.Runnable
if networkOptions.EnableIPPool {
var ippoolProvider ippoolclient.Provider
ippoolProvider = ippoolclient.NewProvider(client.KubeSphere(), networkOptions.IPPoolOptions)
if networkOptions.IPPoolOptions.Calico != nil {
ippoolProvider = calicoclient.NewProvider(client.KubeSphere(), *networkOptions.IPPoolOptions.Calico, options)
}
ippoolProvider := ippoolclient.NewProvider(kubernetesInformer.Core().V1().Pods(), client.KubeSphere(), client.Kubernetes(), networkOptions.IPPoolType, options)
if ippoolProvider != nil {
ippoolController = ippool.NewIPPoolController(kubesphereInformer.Network().V1alpha1().IPPools(),
kubesphereInformer.Network().V1alpha1().IPAMBlocks(),
client.Kubernetes(),
client.KubeSphere(),
networkOptions.IPPoolOptions,
ippoolProvider)
}

View File

@@ -18,6 +18,9 @@ package options
import (
"flag"
"strings"
"time"
"github.com/spf13/pflag"
"k8s.io/client-go/tools/leaderelection"
cliflag "k8s.io/component-base/cli/flag"
@@ -31,8 +34,6 @@ import (
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/simple/client/servicemesh"
"strings"
"time"
)
type KubeSphereControllerManagerOptions struct {

View File

@@ -27,7 +27,7 @@ import (
"kubesphere.io/kubesphere/pkg/apis"
controllerconfig "kubesphere.io/kubesphere/pkg/apiserver/config"
"kubesphere.io/kubesphere/pkg/controller/namespace"
"kubesphere.io/kubesphere/pkg/controller/network/nsnetworkpolicy"
"kubesphere.io/kubesphere/pkg/controller/network/webhooks"
"kubesphere.io/kubesphere/pkg/controller/user"
"kubesphere.io/kubesphere/pkg/controller/workspace"
"kubesphere.io/kubesphere/pkg/controller/workspacerole"
@@ -252,7 +252,8 @@ func run(s *options.KubeSphereControllerManagerOptions, stopCh <-chan struct{})
klog.V(2).Info("registering webhooks to the webhook server")
hookServer.Register("/validate-email-iam-kubesphere-io-v1alpha2-user", &webhook.Admission{Handler: &user.EmailValidator{Client: mgr.GetClient()}})
hookServer.Register("/validate-nsnp-kubesphere-io-v1alpha1-network", &webhook.Admission{Handler: &nsnetworkpolicy.NSNPValidator{Client: mgr.GetClient()}})
hookServer.Register("/validate-network-kubesphere-io-v1alpha1", &webhook.Admission{Handler: &webhooks.ValidatingHandler{C: mgr.GetClient()}})
hookServer.Register("/mutate-network-kubesphere-io-v1alpha1", &webhook.Admission{Handler: &webhooks.MutatingHandler{C: mgr.GetClient()}})
klog.V(0).Info("Starting the controllers.")
if err = mgr.Start(stopCh); err != nil {

View File

@@ -3,14 +3,20 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (devel)
creationTimestamp: null
name: ippools.network.kubesphere.io
spec:
group: network.kubesphere.io
names:
kind: IPPool
listKind: IPPoolList
plural: ippools
singular: ippool
scope: Cluster
subresources:
status: {}
validation:
openAPIV3Schema:
properties:
@@ -36,8 +42,8 @@ spec:
description: The pool CIDR.
type: string
disabled:
description: When disabled is true, IPAM will not assign addresses
from this pool.
description: When disabled is true, IPAM will not assign addresses from
this pool.
type: boolean
dns:
description: DNS contains values interesting for DNS resolvers
@@ -59,11 +65,11 @@ spec:
type: object
gateway:
type: string
namespace:
type: string
rangeEnd:
description: The last ip, inclusive
type: string
rangeStart:
description: The first ip, inclusive
type: string
routes:
items:
@@ -87,8 +93,6 @@ spec:
- master
- vlanId
type: object
workspace:
type: string
required:
- cidr
- type
@@ -96,7 +100,6 @@ spec:
status:
properties:
allocations:
description: Allocations should equal to (Total - Reserved - Unallocated)
type: integer
capacity:
type: integer
@@ -106,6 +109,19 @@ spec:
type: boolean
unallocated:
type: integer
workspaces:
additionalProperties:
properties:
allocations:
type: integer
required:
- allocations
type: object
type: object
required:
- allocations
- capacity
- unallocated
type: object
type: object
version: v1alpha1

View File

@@ -18,11 +18,11 @@ package v1alpha1
import (
"fmt"
"github.com/projectcalico/libcalico-go/lib/names"
"math/big"
"reflect"
"strings"
"github.com/projectcalico/libcalico-go/lib/names"
cnet "github.com/projectcalico/libcalico-go/lib/net"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

View File

@@ -31,15 +31,21 @@ const (
// scope type > id > name
// id used to detect cidr overlap
IPPoolTypeLabel = "ippool.network.kubesphere.io/type"
IPPoolNameLabel = "ippool.network.kubesphere.io/name"
IPPoolIDLabel = "ippool.network.kubesphere.io/id"
IPPoolTypeLabel = "ippool.network.kubesphere.io/type"
IPPoolNameLabel = "ippool.network.kubesphere.io/name"
IPPoolIDLabel = "ippool.network.kubesphere.io/id"
IPPoolDefaultLabel = "ippool.network.kubesphere.io/default"
IPPoolTypeNone = "none"
IPPoolTypeLocal = "local"
IPPoolTypeCalico = "calico"
)
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:openapi-gen=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
type IPPool struct {
metav1.TypeMeta `json:",inline"`
@@ -69,12 +75,17 @@ type DNS struct {
Options []string `json:"options,omitempty"`
}
type WorkspaceStatus struct {
Allocations int `json:"allocations"`
}
type IPPoolStatus struct {
Unallocated int `json:"unallocated,omitempty"`
Allocations int `json:"allocations,omitempty"`
Capacity int `json:"capacity,omitempty"`
Reserved int `json:"reserved,omitempty"`
Synced bool `json:"synced,omitempty"`
Unallocated int `json:"unallocated"`
Allocations int `json:"allocations"`
Capacity int `json:"capacity"`
Reserved int `json:"reserved,omitempty"`
Synced bool `json:"synced,omitempty"`
Workspaces map[string]WorkspaceStatus `json:"workspaces,omitempty"`
}
type IPPoolSpec struct {
@@ -100,9 +111,6 @@ type IPPoolSpec struct {
Gateway string `json:"gateway,omitempty"`
Routes []Route `json:"routes,omitempty"`
DNS DNS `json:"dns,omitempty"`
Workspace string `json:"workspace,omitempty"`
Namespace string `json:"namespace,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -127,9 +135,9 @@ const (
// Find the ordinal (i.e. how far into the block) a given IP lies. Returns an error if the IP is outside the block.
func (b IPPool) IPToOrdinal(ip cnet.IP) (int, error) {
netIP, _, _ := cnet.ParseCIDR(b.Spec.CIDR)
_, cidr, _ := cnet.ParseCIDR(b.Spec.CIDR)
ipAsInt := cnet.IPToBigInt(ip)
baseInt := cnet.IPToBigInt(*netIP)
baseInt := cnet.IPToBigInt(cnet.IP{IP: cidr.IP})
ord := big.NewInt(0).Sub(ipAsInt, baseInt).Int64()
if ord < 0 || ord >= int64(b.NumAddresses()) {
return 0, fmt.Errorf("IP %s not in pool %s", ip, b.Spec.CIDR)
@@ -145,6 +153,14 @@ func (b IPPool) NumAddresses() int {
return numAddresses
}
func (b IPPool) Type() string {
if b.Spec.Type == VLAN {
return IPPoolTypeLocal
}
return b.Spec.Type
}
func (b IPPool) NumReservedAddresses() int {
return b.StartReservedAddressed() + b.EndReservedAddressed()
}
@@ -166,6 +182,17 @@ func (b IPPool) EndReservedAddressed() int {
return total - end - 1
}
func (b IPPool) Overlapped(dst IPPool) bool {
if b.ID() != dst.ID() {
return false
}
_, cidr, _ := cnet.ParseCIDR(b.Spec.CIDR)
_, cidrDst, _ := cnet.ParseCIDR(dst.Spec.CIDR)
return cidr.IsNetOverlap(cidrDst.IPNet)
}
func (pool IPPool) ID() uint32 {
switch pool.Spec.Type {
case VLAN:

View File

@@ -30,7 +30,7 @@ func TestIPPool(t *testing.T) {
},
Spec: IPPoolSpec{
Type: VLAN,
CIDR: "192.168.0.0/24",
CIDR: "192.168.0.1/24",
RangeEnd: "192.168.0.250",
RangeStart: "192.168.0.10",
},

View File

@@ -148,3 +148,7 @@ type NamespaceNetworkPolicyList struct {
metav1.ListMeta `json:"metadata,omitempty"`
Items []NamespaceNetworkPolicy `json:"items"`
}
const (
NSNPPrefix = "nsnp-"
)

View File

@@ -87,6 +87,18 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/apimachinery/pkg/apis/meta/v1.UpdateOptions": schema_pkg_apis_meta_v1_UpdateOptions(ref),
"k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent": schema_pkg_apis_meta_v1_WatchEvent(ref),
"k8s.io/apimachinery/pkg/util/intstr.IntOrString": schema_apimachinery_pkg_util_intstr_IntOrString(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.AllocationAttribute": schema_pkg_apis_network_v1alpha1_AllocationAttribute(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.DNS": schema_pkg_apis_network_v1alpha1_DNS(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlock": schema_pkg_apis_network_v1alpha1_IPAMBlock(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlockList": schema_pkg_apis_network_v1alpha1_IPAMBlockList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlockSpec": schema_pkg_apis_network_v1alpha1_IPAMBlockSpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandle": schema_pkg_apis_network_v1alpha1_IPAMHandle(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandleList": schema_pkg_apis_network_v1alpha1_IPAMHandleList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandleSpec": schema_pkg_apis_network_v1alpha1_IPAMHandleSpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPool": schema_pkg_apis_network_v1alpha1_IPPool(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolList": schema_pkg_apis_network_v1alpha1_IPPoolList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolSpec": schema_pkg_apis_network_v1alpha1_IPPoolSpec(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolStatus": schema_pkg_apis_network_v1alpha1_IPPoolStatus(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicy": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicyList": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicyList(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NamespaceNetworkPolicySpec": schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicySpec(ref),
@@ -94,7 +106,11 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyEgressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyEgressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyIngressRule": schema_pkg_apis_network_v1alpha1_NetworkPolicyIngressRule(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.NetworkPolicyPeer": schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ReservedAttr": schema_pkg_apis_network_v1alpha1_ReservedAttr(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Route": schema_pkg_apis_network_v1alpha1_Route(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.ServiceSelector": schema_pkg_apis_network_v1alpha1_ServiceSelector(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.VLANConfig": schema_pkg_apis_network_v1alpha1_VLANConfig(ref),
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceStatus": schema_pkg_apis_network_v1alpha1_WorkspaceStatus(ref),
}
}
@@ -2515,6 +2531,611 @@ func schema_apimachinery_pkg_util_intstr_IntOrString(ref common.ReferenceCallbac
}
}
func schema_pkg_apis_network_v1alpha1_AllocationAttribute(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"handle_id": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"secondary": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_DNS(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "DNS contains values interesting for DNS resolvers",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"nameservers": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"domain": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"search": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"options": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMBlock(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "Specification of the IPAMBlock.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlockSpec"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlockSpec"},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMBlockList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlock"),
},
},
},
},
},
},
Required: []string{"metadata", "items"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMBlock"},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMBlockSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "IPAMBlockSpec contains the specification for an IPAMBlock resource.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"id": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int64",
},
},
"cidr": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"allocations": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
},
},
},
"unallocated": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
},
},
},
"attributes": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.AllocationAttribute"),
},
},
},
},
},
"deleted": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"id", "cidr", "allocations", "unallocated", "attributes", "deleted"},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.AllocationAttribute"},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMHandle(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata.",
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "Specification of the IPAMHandle.",
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandleSpec"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandleSpec"},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMHandleList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandle"),
},
},
},
},
},
},
Required: []string{"metadata", "items"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPAMHandle"},
}
}
func schema_pkg_apis_network_v1alpha1_IPAMHandleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "IPAMHandleSpec contains the specification for an IPAMHandle resource.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"handleID": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"block": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
},
},
},
"deleted": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
},
Required: []string{"handleID", "block", "deleted"},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_IPPool(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolSpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolStatus"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolSpec", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPoolStatus"},
}
}
func schema_pkg_apis_network_v1alpha1_IPPoolList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPool"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.IPPool"},
}
}
func schema_pkg_apis_network_v1alpha1_IPPoolSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"cidr": {
SchemaProps: spec.SchemaProps{
Description: "The pool CIDR.",
Type: []string{"string"},
Format: "",
},
},
"rangeStart": {
SchemaProps: spec.SchemaProps{
Description: "The first ip, inclusive",
Type: []string{"string"},
Format: "",
},
},
"rangeEnd": {
SchemaProps: spec.SchemaProps{
Description: "The last ip, inclusive",
Type: []string{"string"},
Format: "",
},
},
"disabled": {
SchemaProps: spec.SchemaProps{
Description: "When disabled is true, IPAM will not assign addresses from this pool.",
Type: []string{"boolean"},
Format: "",
},
},
"blockSize": {
SchemaProps: spec.SchemaProps{
Description: "The block size to use for IP address assignments from this pool. Defaults to 26 for IPv4 and 112 for IPv6.",
Type: []string{"integer"},
Format: "int32",
},
},
"vlanConfig": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.VLANConfig"),
},
},
"gateway": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"routes": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Route"),
},
},
},
},
},
"dns": {
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.DNS"),
},
},
},
Required: []string{"type", "cidr"},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.DNS", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.Route", "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.VLANConfig"},
}
}
func schema_pkg_apis_network_v1alpha1_IPPoolStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"unallocated": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
"allocations": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
"capacity": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
"reserved": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
"synced": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
"workspaces": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: ref("kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceStatus"),
},
},
},
},
},
},
Required: []string{"unallocated", "allocations", "capacity"},
},
},
Dependencies: []string{
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1.WorkspaceStatus"},
}
}
func schema_pkg_apis_network_v1alpha1_NamespaceNetworkPolicy(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2788,6 +3409,71 @@ func schema_pkg_apis_network_v1alpha1_NetworkPolicyPeer(ref common.ReferenceCall
}
}
func schema_pkg_apis_network_v1alpha1_ReservedAttr(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"StartOfBlock": {
SchemaProps: spec.SchemaProps{
Description: "Number of addresses reserved from start of the block.",
Type: []string{"integer"},
Format: "int32",
},
},
"EndOfBlock": {
SchemaProps: spec.SchemaProps{
Description: "Number of addresses reserved from end of the block.",
Type: []string{"integer"},
Format: "int32",
},
},
"Handle": {
SchemaProps: spec.SchemaProps{
Description: "Handle for reserved addresses.",
Type: []string{"string"},
Format: "",
},
},
"Note": {
SchemaProps: spec.SchemaProps{
Description: "A description about the reserves.",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"StartOfBlock", "EndOfBlock", "Handle", "Note"},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_Route(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"dst": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"gateway": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_ServiceSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -2812,3 +3498,47 @@ func schema_pkg_apis_network_v1alpha1_ServiceSelector(ref common.ReferenceCallba
},
}
}
func schema_pkg_apis_network_v1alpha1_VLANConfig(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"vlanId": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int64",
},
},
"master": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"vlanId", "master"},
},
},
}
}
func schema_pkg_apis_network_v1alpha1_WorkspaceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"allocations": {
SchemaProps: spec.SchemaProps{
Type: []string{"integer"},
Format: "int32",
},
},
},
Required: []string{"allocations"},
},
},
}
}

View File

@@ -259,7 +259,7 @@ func (in *IPPool) DeepCopyInto(out *IPPool) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPPool.
@@ -337,6 +337,13 @@ func (in *IPPoolSpec) DeepCopy() *IPPoolSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IPPoolStatus) DeepCopyInto(out *IPPoolStatus) {
*out = *in
if in.Workspaces != nil {
in, out := &in.Workspaces, &out.Workspaces
*out = make(map[string]WorkspaceStatus, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPPoolStatus.
@@ -603,3 +610,18 @@ func (in *VLANConfig) DeepCopy() *VLANConfig {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkspaceStatus) DeepCopyInto(out *WorkspaceStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceStatus.
func (in *WorkspaceStatus) DeepCopy() *WorkspaceStatus {
if in == nil {
return nil
}
out := new(WorkspaceStatus)
in.DeepCopyInto(out)
return out
}

View File

@@ -409,6 +409,7 @@ func (s *APIServer) waitForResourceSync(stopCh <-chan struct{}) error {
{Group: "iam.kubesphere.io", Version: "v1alpha2", Resource: "groupbindings"},
{Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"},
{Group: "devops.kubesphere.io", Version: "v1alpha3", Resource: "devopsprojects"},
{Group: "network.kubesphere.io", Version: "v1alpha1", Resource: "ippools"},
}
devopsGVRs := []schema.GroupVersionResource{

View File

@@ -19,6 +19,7 @@ package config
import (
"fmt"
"github.com/spf13/viper"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options"
"kubesphere.io/kubesphere/pkg/simple/client/alerting"
@@ -166,6 +167,28 @@ func (conf *Config) ToMap() map[string]bool {
continue
}
if name == "network" {
ippoolName := "network.ippool"
nsnpName := "network"
if conf.NetworkOptions == nil {
result[nsnpName] = false
result[ippoolName] = false
} else {
if conf.NetworkOptions.EnableNetworkPolicy {
result[nsnpName] = true
} else {
result[nsnpName] = false
}
if conf.NetworkOptions.IPPoolType == networkv1alpha1.IPPoolTypeNone {
result[ippoolName] = false
} else {
result[ippoolName] = true
}
}
continue
}
if c.Field(i).IsNil() {
result[name] = false
} else {

View File

@@ -21,6 +21,7 @@ import (
"github.com/google/go-cmp/cmp"
"gopkg.in/yaml.v2"
"io/ioutil"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/oauth"
authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options"
@@ -106,6 +107,7 @@ func newTestConfig() (*Config, error) {
AllowedIngressNamespaces: []string{},
},
WeaveScopeHost: "weave-scope-app.weave",
IPPoolType: networkv1alpha1.IPPoolTypeNone,
},
MonitoringOptions: &prometheus.Options{
Endpoint: "http://prometheus.kubesphere-monitoring-system.svc",

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())

View File

@@ -18,7 +18,6 @@ package nsnetworkpolicy
import (
"fmt"
"kubesphere.io/kubesphere/pkg/controller/network/types"
"net"
"sort"
"strings"
@@ -62,7 +61,7 @@ const (
NodeNSNPAnnotationKey = "kubesphere.io/snat-node-ips"
AnnotationNPNAME = types.NSNPPrefix + "network-isolate"
AnnotationNPNAME = v1alpha1.NSNPPrefix + "network-isolate"
//TODO: configure it
DNSLocalIP = "169.254.25.10"
@@ -222,7 +221,7 @@ func (c *NSNetworkPolicyController) convertPeer(peer v1alpha1.NetworkPolicyPeer,
func (c *NSNetworkPolicyController) convertToK8sNP(n *v1alpha1.NamespaceNetworkPolicy) (*netv1.NetworkPolicy, error) {
np := &netv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: types.NSNPPrefix + n.Name,
Name: v1alpha1.NSNPPrefix + n.Name,
Namespace: n.Namespace,
},
Spec: netv1.NetworkPolicySpec{
@@ -564,7 +563,7 @@ func (c *NSNetworkPolicyController) syncNSNP(key string) error {
if err != nil {
if errors.IsNotFound(err) {
klog.V(4).Infof("NSNP %v has been deleted", key)
c.provider.Delete(c.provider.GetKey(types.NSNPPrefix+name, namespace))
c.provider.Delete(c.provider.GetKey(v1alpha1.NSNPPrefix+name, namespace))
return nil
}

View File

@@ -19,7 +19,6 @@ package provider
import (
"context"
"fmt"
"kubesphere.io/kubesphere/pkg/controller/network/types"
"reflect"
"strings"
"sync"
@@ -36,6 +35,7 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
)
const (
@@ -246,7 +246,7 @@ func NewNsNetworkPolicyProvider(client kubernetes.Interface, npInformer informer
// Filter in only objects that are written by policy controller.
m := make(map[string]interface{})
for _, policy := range policies {
if strings.HasPrefix(policy.Name, types.NSNPPrefix) {
if strings.HasPrefix(policy.Name, v1alpha1.NSNPPrefix) {
policy.ObjectMeta = metav1.ObjectMeta{Name: policy.Name, Namespace: policy.Namespace}
k := c.GetKey(policy.Name, policy.Namespace)
m[k] = *policy

View File

@@ -1,190 +0,0 @@
/*
Copyright 2020 KubeSphere Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nsnetworkpolicy
import (
"context"
corev1 "k8s.io/api/core/v1"
k8snet "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"net"
"net/http"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
type NSNPValidator struct {
Client client.Client
decoder *admission.Decoder
}
func (v *NSNPValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
nsnp := &networkv1alpha1.NamespaceNetworkPolicy{}
err := v.decoder.Decode(req, nsnp)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
allErrs := field.ErrorList{}
allErrs = append(allErrs, v.ValidateNSNPSpec(&nsnp.Spec, field.NewPath("spec"))...)
if len(allErrs) != 0 {
return admission.Denied(allErrs.ToAggregate().Error())
}
return admission.Allowed("")
}
func (v *NSNPValidator) InjectDecoder(d *admission.Decoder) error {
v.decoder = d
return nil
}
// ValidateNetworkPolicyPort validates a NetworkPolicyPort
func (v *NSNPValidator) ValidateNetworkPolicyPort(port *k8snet.NetworkPolicyPort, portPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if port.Protocol != nil && *port.Protocol != corev1.ProtocolTCP && *port.Protocol != corev1.ProtocolUDP && *port.Protocol != corev1.ProtocolSCTP {
allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(corev1.ProtocolTCP), string(corev1.ProtocolUDP), string(corev1.ProtocolSCTP)}))
}
if port.Port != nil {
if port.Port.Type == intstr.Int {
for _, msg := range validation.IsValidPortNum(int(port.Port.IntVal)) {
allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.IntVal, msg))
}
} else {
for _, msg := range validation.IsValidPortName(port.Port.StrVal) {
allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.StrVal, msg))
}
}
}
return allErrs
}
func (v *NSNPValidator) ValidateServiceSelector(serviceSelector *networkv1alpha1.ServiceSelector, fldPath *field.Path) field.ErrorList {
service := &corev1.Service{}
allErrs := field.ErrorList{}
err := v.Client.Get(context.TODO(), client.ObjectKey{Namespace: serviceSelector.Namespace, Name: serviceSelector.Name}, service)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, serviceSelector, "cannot get service"))
return allErrs
}
if len(service.Spec.Selector) <= 0 {
allErrs = append(allErrs, field.Invalid(fldPath, serviceSelector, "service should have selector"))
}
return allErrs
}
// ValidateCIDR validates whether a CIDR matches the conventions expected by net.ParseCIDR
func ValidateCIDR(cidr string) (*net.IPNet, error) {
_, net, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
return net, nil
}
// ValidateIPBlock validates a cidr and the except fields of an IpBlock NetworkPolicyPeer
func (v *NSNPValidator) ValidateIPBlock(ipb *k8snet.IPBlock, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(ipb.CIDR) == 0 || ipb.CIDR == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), ""))
return allErrs
}
cidrIPNet, err := ValidateCIDR(ipb.CIDR)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), ipb.CIDR, "not a valid CIDR"))
return allErrs
}
exceptCIDR := ipb.Except
for i, exceptIP := range exceptCIDR {
exceptPath := fldPath.Child("except").Index(i)
exceptCIDR, err := ValidateCIDR(exceptIP)
if err != nil {
allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "not a valid CIDR"))
return allErrs
}
if !cidrIPNet.Contains(exceptCIDR.IP) {
allErrs = append(allErrs, field.Invalid(exceptPath, exceptCIDR.IP, "not within CIDR range"))
}
}
return allErrs
}
// ValidateNSNPPeer validates a NetworkPolicyPeer
func (v *NSNPValidator) ValidateNSNPPeer(peer *networkv1alpha1.NetworkPolicyPeer, peerPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
numPeers := 0
if peer.ServiceSelector != nil {
numPeers++
allErrs = append(allErrs, v.ValidateServiceSelector(peer.ServiceSelector, peerPath.Child("service"))...)
}
if peer.NamespaceSelector != nil {
numPeers++
}
if peer.IPBlock != nil {
numPeers++
allErrs = append(allErrs, v.ValidateIPBlock(peer.IPBlock, peerPath.Child("ipBlock"))...)
}
if numPeers == 0 {
allErrs = append(allErrs, field.Required(peerPath, "must specify a peer"))
} else if numPeers > 1 && peer.IPBlock != nil {
allErrs = append(allErrs, field.Forbidden(peerPath, "may not specify both ipBlock and another peer"))
}
return allErrs
}
func (v *NSNPValidator) ValidateNSNPSpec(spec *networkv1alpha1.NamespaceNetworkPolicySpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// Validate ingress rules.
for i, ingress := range spec.Ingress {
ingressPath := fldPath.Child("ingress").Index(i)
for i, port := range ingress.Ports {
portPath := ingressPath.Child("ports").Index(i)
allErrs = append(allErrs, v.ValidateNetworkPolicyPort(&port, portPath)...)
}
for i, from := range ingress.From {
fromPath := ingressPath.Child("from").Index(i)
allErrs = append(allErrs, v.ValidateNSNPPeer(&from, fromPath)...)
}
}
// Validate egress rules
for i, egress := range spec.Egress {
egressPath := fldPath.Child("egress").Index(i)
for i, port := range egress.Ports {
portPath := egressPath.Child("ports").Index(i)
allErrs = append(allErrs, v.ValidateNetworkPolicyPort(&port, portPath)...)
}
for i, to := range egress.To {
toPath := egressPath.Child("to").Index(i)
allErrs = append(allErrs, v.ValidateNSNPPeer(&to, toPath)...)
}
}
return allErrs
}

View File

@@ -1,21 +0,0 @@
/*
Copyright 2020 KubeSphere Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package types
const (
NSNPPrefix = "nsnp-"
)

View File

@@ -0,0 +1,97 @@
package webhooks
import (
"context"
"encoding/json"
"net/http"
"sync"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
// Defaulter defines functions for setting defaults on resources
type Defaulter interface {
Default(obj runtime.Object) error
}
type DefaulterWrap struct {
Obj runtime.Object
Helper Defaulter
}
type MutatingHandler struct {
C client.Client
decoder *admission.Decoder
}
var _ admission.DecoderInjector = &MutatingHandler{}
// InjectDecoder injects the decoder into a MutatingHandler.
func (h *MutatingHandler) InjectDecoder(d *admission.Decoder) error {
h.decoder = d
return nil
}
type defaulters struct {
ds map[string]*DefaulterWrap
lock sync.RWMutex
}
var (
ds defaulters
)
func init() {
ds = defaulters{
ds: make(map[string]*DefaulterWrap),
lock: sync.RWMutex{},
}
}
func RegisterDefaulter(name string, d *DefaulterWrap) {
ds.lock.Lock()
defer ds.lock.Unlock()
ds.ds[name] = d
}
func UnRegisterDefaulter(name string) {
ds.lock.Lock()
defer ds.lock.Unlock()
delete(ds.ds, name)
}
func GetDefaulter(name string) *DefaulterWrap {
ds.lock.Lock()
defer ds.lock.Unlock()
return ds.ds[name]
}
// Handle handles admission requests.
func (h *MutatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
defaulter := GetDefaulter(req.Kind.String())
if defaulter == nil {
return admission.Denied("crd has webhook configured, but the controller does not register the corresponding processing logic and refuses the operation by default.")
}
// Get the object in the request
obj := defaulter.Obj.DeepCopyObject()
err := h.decoder.Decode(req, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
// Default the object
defaulter.Helper.Default(obj)
marshalled, err := json.Marshal(obj)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
// Create the patch
return admission.PatchResponseFromRaw(req.Object.Raw, marshalled)
}

View File

@@ -0,0 +1,146 @@
/*
Copyright 2020 KubeSphere Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package webhooks
import (
"context"
"net/http"
"sync"
"k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
// Validator defines functions for validating an operation
type Validator interface {
ValidateCreate(obj runtime.Object) error
ValidateUpdate(old runtime.Object, new runtime.Object) error
ValidateDelete(obj runtime.Object) error
}
type ValidatorWrap struct {
Obj runtime.Object
Helper Validator
}
type validators struct {
vs map[string]*ValidatorWrap
lock sync.RWMutex
}
var (
vs validators
)
func init() {
vs = validators{
vs: make(map[string]*ValidatorWrap),
lock: sync.RWMutex{},
}
}
func RegisterValidator(name string, v *ValidatorWrap) {
vs.lock.Lock()
defer vs.lock.Unlock()
vs.vs[name] = v
}
func UnRegisterValidator(name string) {
vs.lock.Lock()
defer vs.lock.Unlock()
delete(vs.vs, name)
}
func GetValidator(name string) *ValidatorWrap {
vs.lock.Lock()
defer vs.lock.Unlock()
return vs.vs[name]
}
type ValidatingHandler struct {
C client.Client
decoder *admission.Decoder
}
var _ admission.DecoderInjector = &ValidatingHandler{}
// InjectDecoder injects the decoder into a ValidatingHandler.
func (h *ValidatingHandler) InjectDecoder(d *admission.Decoder) error {
h.decoder = d
return nil
}
// Handle handles admission requests.
func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
validator := GetValidator(req.Kind.String())
if validator == nil {
return admission.Denied("crd has webhook configured, but the controller does not register the corresponding processing logic and refuses the operation by default.")
}
// Get the object in the request
obj := validator.Obj.DeepCopyObject()
if req.Operation == v1beta1.Create {
err := h.decoder.Decode(req, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
err = validator.Helper.ValidateCreate(obj)
if err != nil {
return admission.Denied(err.Error())
}
}
if req.Operation == v1beta1.Update {
oldObj := obj.DeepCopyObject()
err := h.decoder.DecodeRaw(req.Object, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
err = h.decoder.DecodeRaw(req.OldObject, oldObj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
err = validator.Helper.ValidateUpdate(oldObj, obj)
if err != nil {
return admission.Denied(err.Error())
}
}
if req.Operation == v1beta1.Delete {
// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
// OldObject contains the object being deleted
err := h.decoder.DecodeRaw(req.OldObject, obj)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
err = validator.Helper.ValidateDelete(obj)
if err != nil {
return admission.Denied(err.Error())
}
}
return admission.Allowed("")
}

View File

@@ -0,0 +1,115 @@
/*
Copyright 2020 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ippool
import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
k8sinformers "k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/api"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/query"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type ippoolGetter struct {
informers informers.SharedInformerFactory
k8sInformers k8sinformers.SharedInformerFactory
}
func New(informers informers.SharedInformerFactory, k8sInformers k8sinformers.SharedInformerFactory) v1alpha3.Interface {
return &ippoolGetter{
informers: informers,
k8sInformers: k8sInformers,
}
}
func (n ippoolGetter) Get(namespace, name string) (runtime.Object, error) {
return n.informers.Network().V1alpha1().IPPools().Lister().Get(name)
}
func (n ippoolGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
var result []runtime.Object
if namespace != "" {
workspace := ""
ns, err := n.k8sInformers.Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
return nil, err
}
if ns.Labels != nil {
workspace = ns.Labels[constants.WorkspaceLabelKey]
}
ps, err := n.informers.Network().V1alpha1().IPPools().Lister().List(labels.SelectorFromSet(
map[string]string{
networkv1alpha1.IPPoolDefaultLabel: "",
}))
if err != nil {
return nil, err
}
for _, p := range ps {
result = append(result, p)
}
if workspace != "" {
query.LabelSelector = labels.SelectorFromSet(
map[string]string{
constants.WorkspaceLabelKey: workspace,
}).String()
ps, err := n.informers.Network().V1alpha1().IPPools().Lister().List(query.Selector())
if err != nil {
return nil, err
}
for _, p := range ps {
result = append(result, p)
}
}
} else {
ps, err := n.informers.Network().V1alpha1().IPPools().Lister().List(labels.Everything())
if err != nil {
return nil, err
}
for _, p := range ps {
result = append(result, p)
}
}
return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil
}
func (n ippoolGetter) filter(item runtime.Object, filter query.Filter) bool {
p, ok := item.(*networkv1alpha1.IPPool)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(p.ObjectMeta, filter)
}
func (n ippoolGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftP, ok := left.(*networkv1alpha1.IPPool)
if !ok {
return false
}
rightP, ok := right.(*networkv1alpha1.IPPool)
if !ok {
return true
}
return v1alpha3.DefaultObjectMetaCompare(leftP.ObjectMeta, rightP.ObjectMeta, field)
}

View File

@@ -0,0 +1,149 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ippool
import (
"github.com/google/go-cmp/cmp"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sinformers "k8s.io/client-go/informers"
k8sfake "k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListIPPools(t *testing.T) {
tests := []struct {
description string
namespace string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
"",
&query.Query{
Pagination: &query.Pagination{
Limit: 10,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{
query.FieldName: query.Value("foo2"),
},
},
&api.ListResult{
Items: []interface{}{foo2},
TotalItems: 1,
},
nil,
},
{
"test namespace filter",
"ns1",
&query.Query{
Pagination: &query.Pagination{
Limit: 10,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
},
&api.ListResult{
Items: []interface{}{foo1},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
got, err := getter.List(test.namespace, test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
}
}
var (
foo1 = &v1alpha1.IPPool{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
Labels: map[string]string{
constants.WorkspaceLabelKey: "wk1",
},
},
}
foo2 = &v1alpha1.IPPool{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
},
}
foo3 = &v1alpha1.IPPool{
ObjectMeta: metav1.ObjectMeta{
Name: "foo3",
},
}
ns = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "ns1",
Labels: map[string]string{
constants.WorkspaceLabelKey: "wk1",
},
},
}
wk = &tenantv1alpha1.Workspace{
ObjectMeta: metav1.ObjectMeta{
Name: "wk1",
},
}
ps = []interface{}{foo1, foo2, foo3}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
k8sClient := k8sfake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
k8sInformer := k8sinformers.NewSharedInformerFactory(k8sClient, 0)
for _, p := range ps {
informer.Network().V1alpha1().IPPools().Informer().GetIndexer().Add(p)
}
informer.Tenant().V1alpha1().Workspaces().Informer().GetIndexer().Add(wk)
k8sInformer.Core().V1().Namespaces().Informer().GetIndexer().Add(ns)
return New(informer, k8sInformer)
}

View File

@@ -26,6 +26,7 @@ import (
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
@@ -55,6 +56,7 @@ import (
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/group"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/groupbinding"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/ingress"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/ippool"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/job"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/loginrecord"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace"
@@ -99,6 +101,7 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
// kubesphere resources
getters[devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject)] = devops.New(factory.KubeSphereSharedInformerFactory())
getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace)] = workspace.New(factory.KubeSphereSharedInformerFactory())
getters[networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralIPPool)] = ippool.New(factory.KubeSphereSharedInformerFactory(), factory.KubernetesSharedInformerFactory())
getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha2.ResourcePluralWorkspaceTemplate)] = workspacetemplate.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRole)] = globalrole.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRole)] = workspacerole.New(factory.KubeSphereSharedInformerFactory())

View File

@@ -17,31 +17,64 @@ limitations under the License.
package calico
import (
"encoding/json"
"errors"
"fmt"
"net"
"time"
v3 "github.com/projectcalico/libcalico-go/lib/apis/v3"
"github.com/projectcalico/libcalico-go/lib/backend/model"
cnet "github.com/projectcalico/libcalico-go/lib/net"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
informercorev1 "k8s.io/client-go/informers/core/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/util/workqueue"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/calicov3"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
calicoset "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico/client/clientset/versioned"
calicoInformer "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico/client/informers/externalversions"
blockInformer "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico/client/informers/externalversions/network/calicov3"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
const (
CalicoNamespaceAnnotationIPPoolV4 = "cni.projectcalico.org/ipv4pools"
CalicoNamespaceAnnotationIPPoolV6 = "cni.projectcalico.org/ipv6pools"
CalicoPodAnnotationIPAddr = "cni.projectcalico.org/ipAddrs"
CalicoAnnotationIPPoolV4 = "cni.projectcalico.org/ipv4pools"
CalicoAnnotationIPPoolV6 = "cni.projectcalico.org/ipv6pools"
CalicoPodAnnotationIPAddr = "cni.projectcalico.org/ipAddrs"
CalicoPodAnnotationPodIP = "cni.projectcalico.org/podIP"
// Common attributes which may be set on allocations by clients.
IPAMBlockAttributePod = "pod"
IPAMBlockAttributeNamespace = "namespace"
IPAMBlockAttributeNode = "node"
IPAMBlockAttributeType = "type"
IPAMBlockAttributeTypeIPIP = "ipipTunnelAddress"
IPAMBlockAttributeTypeVXLAN = "vxlanTunnelAddress"
CALICO_IPV4POOL_IPIP = "CALICO_IPV4POOL_IPIP"
CALICO_IPV4POOL_VXLAN = "CALICO_IPV4POOL_VXLAN"
CALICO_IPV4POOL_NAT_OUTGOING = "CALICO_IPV4POOL_NAT_OUTGOING"
CalicoNodeDaemonset = "calico-node"
CalicoNodeNamespace = "kube-system"
DefaultBlockSize = 25
// default re-sync period for all informer factories
defaultResync = 600 * time.Second
)
var (
@@ -49,9 +82,15 @@ var (
)
type provider struct {
client calicoset.Interface
ksclient kubesphereclient.Interface
options Options
client calicoset.Interface
ksclient kubesphereclient.Interface
k8sclient clientset.Interface
pods informercorev1.PodInformer
block blockInformer.IPAMBlockInformer
queue workqueue.RateLimitingInterface
poolQueue workqueue.RateLimitingInterface
options Options
}
func (c provider) CreateIPPool(pool *v1alpha1.IPPool) error {
@@ -70,6 +109,12 @@ func (c provider) CreateIPPool(pool *v1alpha1.IPPool) error {
},
}
_, cidr, _ := net.ParseCIDR(pool.Spec.CIDR)
size, _ := cidr.Mask.Size()
if size > DefaultBlockSize {
calicoPool.Spec.BlockSize = size
}
err := controllerutil.SetControllerReference(pool, calicoPool, scheme.Scheme)
if err != nil {
klog.Warningf("cannot set reference for calico ippool %s, err=%v", pool.Name, err)
@@ -88,7 +133,7 @@ func (c provider) UpdateIPPool(pool *v1alpha1.IPPool) error {
}
func (c provider) GetIPPoolStats(pool *v1alpha1.IPPool) (*v1alpha1.IPPool, error) {
stats := &v1alpha1.IPPool{}
stats := pool.DeepCopy()
calicoPool, err := c.client.CrdCalicov3().IPPools().Get(pool.Name, v1.GetOptions{})
if err != nil {
@@ -100,24 +145,46 @@ func (c provider) GetIPPoolStats(pool *v1alpha1.IPPool) (*v1alpha1.IPPool, error
return nil, err
}
stats.Status.Capacity = pool.NumAddresses()
stats.Status.Reserved = 0
stats.Status.Unallocated = 0
if stats.Status.Capacity == 0 {
stats.Status.Capacity = pool.NumAddresses()
}
stats.Status.Synced = true
stats.Status.Allocations = 0
stats.Status.Reserved = 0
if stats.Status.Workspaces == nil {
stats.Status.Workspaces = make(map[string]v1alpha1.WorkspaceStatus)
}
if len(blocks) <= 0 {
stats.Status.Unallocated = pool.NumAddresses()
stats.Status.Allocations = 0
return stats, nil
} else {
for _, block := range blocks {
stats.Status.Allocations += block.NumAddresses() - block.NumFreeAddresses() - block.NumReservedAddresses()
stats.Status.Reserved += block.NumReservedAddresses()
}
stats.Status.Unallocated = stats.Status.Capacity - stats.Status.Allocations - stats.Status.Reserved
}
for _, block := range blocks {
stats.Status.Allocations += block.NumAddresses() - block.NumFreeAddresses() - block.NumReservedAddresses()
stats.Status.Reserved += block.NumReservedAddresses()
wks, err := c.getAssociatedWorkspaces(pool)
if err != nil {
return nil, err
}
stats.Status.Unallocated = stats.Status.Capacity - stats.Status.Allocations - stats.Status.Reserved
for _, wk := range wks {
status, err := c.getWorkspaceStatus(wk, pool.GetName())
if err != nil {
return nil, err
}
stats.Status.Workspaces[wk] = *status
}
for name, wk := range stats.Status.Workspaces {
if wk.Allocations == 0 {
delete(stats.Status.Workspaces, name)
}
}
return stats, nil
}
@@ -240,6 +307,9 @@ func (c provider) DeleteIPPool(pool *v1alpha1.IPPool) (bool, error) {
// Get the pool so that we can find the CIDR associated with it.
calicoPool, err := c.client.CrdCalicov3().IPPools().Get(pool.Name, v1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
return true, nil
}
return false, err
}
@@ -318,6 +388,9 @@ func (c provider) syncIPPools() error {
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Name: calicoPool.Name,
Labels: map[string]string{
v1alpha1.IPPoolDefaultLabel: "",
},
},
Spec: v1alpha1.IPPoolSpec{
Type: v1alpha1.Calico,
@@ -339,57 +412,233 @@ func (c provider) syncIPPools() error {
return nil
}
func (c provider) SyncStatus(stopCh <-chan struct{}, q workqueue.RateLimitingInterface) error {
blockWatch, err := c.client.CrdCalicov3().IPAMBlocks().Watch(v1.ListOptions{})
if err != nil {
return err
func (p provider) getAssociatedWorkspaces(pool *v1alpha1.IPPool) ([]string, error) {
var result []string
poolLabel := constants.WorkspaceLabelKey
if pool.GetLabels() == nil || pool.GetLabels()[poolLabel] == "" {
wks, err := p.ksclient.TenantV1alpha1().Workspaces().List(v1.ListOptions{})
if err != nil {
return nil, err
}
for _, wk := range wks.Items {
result = append(result, wk.GetName())
}
return result, nil
}
ch := blockWatch.ResultChan()
defer blockWatch.Stop()
return append(result, pool.GetLabels()[poolLabel]), nil
}
for {
select {
case <-stopCh:
return nil
case event, ok := <-ch:
if !ok {
// End of results.
return fmt.Errorf("calico ipamblock watch closed")
}
func (p provider) getWorkspaceStatus(name string, poolName string) (*v1alpha1.WorkspaceStatus, error) {
var result v1alpha1.WorkspaceStatus
if event.Type == watch.Added || event.Type == watch.Deleted || event.Type == watch.Modified {
block := event.Object.(*calicov3.IPAMBlock)
_, blockCIDR, _ := cnet.ParseCIDR(block.Spec.CIDR)
namespaces, err := p.k8sclient.CoreV1().Namespaces().List(v1.ListOptions{
LabelSelector: labels.SelectorFromSet(
map[string]string{
constants.WorkspaceLabelKey: name,
},
).String(),
})
if err != nil {
return nil, err
}
if block.Labels[v1alpha1.IPPoolNameLabel] != "" {
q.Add(block.Labels[v1alpha1.IPPoolNameLabel])
continue
}
pools, err := c.ksclient.NetworkV1alpha1().IPPools().List(v1.ListOptions{})
if err != nil {
continue
}
for _, pool := range pools.Items {
_, poolCIDR, _ := cnet.ParseCIDR(pool.Spec.CIDR)
if poolCIDR.IsNetOverlap(blockCIDR.IPNet) {
q.Add(pool.Name)
block.Labels = map[string]string{
v1alpha1.IPPoolNameLabel: pool.Name,
}
c.client.CrdCalicov3().IPAMBlocks().Update(block)
break
}
}
for _, ns := range namespaces.Items {
pods, err := p.k8sclient.CoreV1().Pods(ns.GetName()).List(v1.ListOptions{})
if err != nil {
return nil, err
}
for _, pod := range pods.Items {
if pod.GetLabels() != nil && pod.GetLabels()[v1alpha1.IPPoolNameLabel] == poolName {
result.Allocations++
}
}
}
return &result, nil
}
func NewProvider(ksclient kubesphereclient.Interface, options Options, k8sOptions *k8s.KubernetesOptions) provider {
func (p provider) Type() string {
return v1alpha1.IPPoolTypeCalico
}
func (p provider) SyncStatus(stopCh <-chan struct{}, q workqueue.RateLimitingInterface) error {
defer utilruntime.HandleCrash()
defer p.queue.ShutDown()
klog.Info("starting calico block controller")
defer klog.Info("shutting down calico block controller")
p.poolQueue = q
go p.block.Informer().Run(stopCh)
if !cache.WaitForCacheSync(stopCh, p.pods.Informer().HasSynced, p.block.Informer().HasSynced) {
klog.Fatal("failed to wait for caches to sync")
}
for i := 0; i < 5; i++ {
go wait.Until(p.runWorker, time.Second, stopCh)
}
<-stopCh
return nil
}
func (p provider) processBlock(name string) error {
block, err := p.block.Lister().Get(name)
if err != nil {
if k8serrors.IsNotFound(err) {
return nil
}
return err
}
_, blockCIDR, _ := cnet.ParseCIDR(block.Spec.CIDR)
poolName := block.Labels[v1alpha1.IPPoolNameLabel]
if poolName == "" {
pools, err := p.ksclient.NetworkV1alpha1().IPPools().List(v1.ListOptions{})
if err != nil {
return err
}
for _, pool := range pools.Items {
_, poolCIDR, _ := cnet.ParseCIDR(pool.Spec.CIDR)
if poolCIDR.IsNetOverlap(blockCIDR.IPNet) {
poolName = pool.Name
block.Labels = map[string]string{
v1alpha1.IPPoolNameLabel: pool.Name,
}
p.client.CrdCalicov3().IPAMBlocks().Update(block)
break
}
}
}
for _, podAttr := range block.Spec.Attributes {
name := podAttr.AttrSecondary[IPAMBlockAttributePod]
namespace := podAttr.AttrSecondary[IPAMBlockAttributeNamespace]
if name == "" || namespace == "" {
continue
}
pod, err := p.pods.Lister().Pods(namespace).Get(name)
if err != nil {
continue
}
labels := pod.GetLabels()
if labels != nil {
poolLabel := labels[v1alpha1.IPPoolNameLabel]
if poolLabel != "" {
continue
}
}
retry.RetryOnConflict(retry.DefaultBackoff, func() error {
pod, err = p.k8sclient.CoreV1().Pods(namespace).Get(name, v1.GetOptions{})
if err != nil {
return err
}
labels := pod.GetLabels()
if labels != nil {
poolLabel := labels[v1alpha1.IPPoolNameLabel]
if poolLabel != "" {
return nil
}
} else {
pod.Labels = make(map[string]string)
}
if pod.GetAnnotations() == nil {
pod.Annotations = make(map[string]string)
}
annostrs, _ := json.Marshal([]string{poolName})
pod.GetAnnotations()[CalicoAnnotationIPPoolV4] = string(annostrs)
pod.Labels[v1alpha1.IPPoolNameLabel] = poolName
_, err = p.k8sclient.CoreV1().Pods(namespace).Update(pod)
return err
})
}
p.poolQueue.Add(poolName)
return nil
}
func (p provider) processBlockItem() bool {
key, quit := p.queue.Get()
if quit {
return false
}
defer p.queue.Done(key)
err := p.processBlock(key.(string))
if err == nil {
p.queue.Forget(key)
return true
}
utilruntime.HandleError(fmt.Errorf("error processing calico block %v (will retry): %v", key, err))
p.queue.AddRateLimited(key)
return true
}
func (p provider) runWorker() {
for p.processBlockItem() {
}
}
func (p provider) addBlock(obj interface{}) {
block, ok := obj.(*calicov3.IPAMBlock)
if !ok {
return
}
p.queue.Add(block.Name)
}
func (p provider) Default(obj runtime.Object) error {
pod, ok := obj.(*corev1.Pod)
if !ok {
return nil
}
annos := pod.GetAnnotations()
if annos == nil {
pod.Annotations = make(map[string]string)
}
if annos[CalicoAnnotationIPPoolV4] == "" {
pools, err := p.ksclient.NetworkV1alpha1().IPPools().List(v1.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{
v1alpha1.IPPoolDefaultLabel: "",
}).String(),
})
if err != nil {
return err
}
var poolNames []string
for _, pool := range pools.Items {
poolNames = append(poolNames, pool.Name)
}
if len(poolNames) > 0 {
annostrs, _ := json.Marshal(poolNames)
pod.Annotations[CalicoAnnotationIPPoolV4] = string(annostrs)
}
}
return nil
}
func NewProvider(podInformer informercorev1.PodInformer, ksclient kubesphereclient.Interface, k8sClient clientset.Interface, k8sOptions *k8s.KubernetesOptions) provider {
config, err := clientcmd.BuildConfigFromFlags("", k8sOptions.KubeConfig)
if err != nil {
klog.Fatalf("failed to build k8s config , err=%v", err)
@@ -401,11 +650,49 @@ func NewProvider(ksclient kubesphereclient.Interface, options Options, k8sOption
klog.Fatalf("failed to new calico client , err=%v", err)
}
p := provider{
client: client,
ksclient: ksclient,
options: options,
ds, err := k8sClient.AppsV1().DaemonSets(CalicoNodeNamespace).Get(CalicoNodeDaemonset, v1.GetOptions{})
if err != nil {
klog.Fatalf("failed to get calico-node deployment in kube-system, err=%v", err)
}
opts := Options{
IPIPMode: "Always",
VXLANMode: "Never",
NATOutgoing: true,
}
envs := ds.Spec.Template.Spec.Containers[0].Env
for _, env := range envs {
if env.Name == CALICO_IPV4POOL_IPIP {
opts.IPIPMode = env.Value
}
if env.Name == CALICO_IPV4POOL_VXLAN {
opts.VXLANMode = env.Value
}
if env.Name == CALICO_IPV4POOL_NAT_OUTGOING {
if env.Value != "true" {
opts.NATOutgoing = false
}
}
}
p := provider{
client: client,
ksclient: ksclient,
k8sclient: k8sClient,
pods: podInformer,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "calicoBlock"),
options: opts,
}
blockI := calicoInformer.NewSharedInformerFactory(client, defaultResync).Crd().Calicov3().IPAMBlocks()
blockI.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: p.addBlock,
UpdateFunc: func(old, new interface{}) {
p.addBlock(new)
},
})
p.block = blockI
if err := p.syncIPPools(); err != nil {
klog.Fatalf("failed to sync calico ippool to kubesphere ippool, err=%v", err)

View File

@@ -18,14 +18,17 @@ package calico
import (
"flag"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
ksfake "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/constants"
calicofake "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico/client/clientset/versioned/fake"
"testing"
)
func TestCalicoIPPoolSuit(t *testing.T) {
@@ -43,6 +46,9 @@ var _ = Describe("test calico ippool", func() {
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Name: "testippool",
Labels: map[string]string{
constants.WorkspaceLabelKey: "wk1",
},
},
Spec: v1alpha1.IPPoolSpec{
Type: v1alpha1.Calico,
@@ -52,7 +58,23 @@ var _ = Describe("test calico ippool", func() {
Status: v1alpha1.IPPoolStatus{},
}
ksclient := ksfake.NewSimpleClientset(pool)
wk1 := &tenantv1alpha1.Workspace{
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Name: "wk1",
},
Spec: tenantv1alpha1.WorkspaceSpec{},
Status: tenantv1alpha1.WorkspaceStatus{},
}
wk2 := &tenantv1alpha1.Workspace{
TypeMeta: v1.TypeMeta{},
ObjectMeta: v1.ObjectMeta{
Name: "wk2",
},
Spec: tenantv1alpha1.WorkspaceSpec{},
Status: tenantv1alpha1.WorkspaceStatus{},
}
ksclient := ksfake.NewSimpleClientset(pool, wk1, wk2)
client := calicofake.NewSimpleClientset()
p := provider{
@@ -69,4 +91,15 @@ var _ = Describe("test calico ippool", func() {
err := p.CreateIPPool(pool)
Expect(err).ShouldNot(HaveOccurred())
})
It("test get workspace", func() {
result, err := p.getAssociatedWorkspaces(pool)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(result)).Should(Equal(1))
pool.Labels = nil
result, err = p.getAssociatedWorkspaces(pool)
Expect(err).ShouldNot(HaveOccurred())
Expect(len(result)).Should(Equal(2))
})
})

View File

@@ -17,9 +17,14 @@ limitations under the License.
package ippool
import (
"k8s.io/apimachinery/pkg/runtime"
v1 "k8s.io/client-go/informers/core/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/workqueue"
networkv1alpha1 "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
calicoclient "kubesphere.io/kubesphere/pkg/simple/client/network/ippool/calico"
"kubesphere.io/kubesphere/pkg/simple/client/network/ippool/ipam"
)
@@ -30,6 +35,8 @@ type Provider interface {
UpdateIPPool(pool *networkv1alpha1.IPPool) error
GetIPPoolStats(pool *networkv1alpha1.IPPool) (*networkv1alpha1.IPPool, error)
SyncStatus(stopCh <-chan struct{}, q workqueue.RateLimitingInterface) error
Type() string
Default(obj runtime.Object) error
}
type provider struct {
@@ -37,6 +44,14 @@ type provider struct {
ipamclient ipam.IPAMClient
}
func (p provider) Type() string {
return networkv1alpha1.IPPoolTypeLocal
}
func (p provider) Default(obj runtime.Object) error {
return nil
}
func (p provider) DeleteIPPool(pool *networkv1alpha1.IPPool) (bool, error) {
blocks, err := p.ipamclient.ListBlocks(pool.Name)
if err != nil {
@@ -77,22 +92,36 @@ func (p provider) GetIPPoolStats(pool *networkv1alpha1.IPPool) (*networkv1alpha1
}
stat := stats[0]
return &networkv1alpha1.IPPool{
Status: networkv1alpha1.IPPoolStatus{
Allocations: stat.Allocate,
Unallocated: stat.Unallocated,
Reserved: stat.Reserved,
Capacity: stat.Capacity,
Synced: true,
},
}, nil
clone := pool.DeepCopy()
clone.Status = networkv1alpha1.IPPoolStatus{
Allocations: stat.Allocate,
Unallocated: stat.Unallocated,
Reserved: stat.Reserved,
Capacity: stat.Capacity,
Synced: true,
}
return clone, nil
}
func NewProvider(clientset kubesphereclient.Interface, options Options) provider {
vlanProvider := provider{
func newProvider(clientset kubesphereclient.Interface) provider {
return provider{
kubesphereClient: clientset,
ipamclient: ipam.NewIPAMClient(clientset, networkv1alpha1.VLAN),
}
return vlanProvider
}
func NewProvider(podInformer v1.PodInformer, clientset kubesphereclient.Interface, client clientset.Interface, pt string, k8sOptions *k8s.KubernetesOptions) Provider {
var p Provider
switch pt {
case networkv1alpha1.IPPoolTypeLocal:
p = provider{
kubesphereClient: clientset,
ipamclient: ipam.NewIPAMClient(clientset, networkv1alpha1.VLAN),
}
case networkv1alpha1.IPPoolTypeCalico:
p = calicoclient.NewProvider(podInformer, clientset, client, k8sOptions)
}
return p
}

View File

@@ -26,7 +26,7 @@ import (
)
func testNewProvider() provider {
return NewProvider(fakeks.NewSimpleClientset(), Options{})
return newProvider(fakeks.NewSimpleClientset())
}
func TestProvider_GetIPPoolStats(t *testing.T) {

View File

@@ -18,8 +18,6 @@ package network
import (
"github.com/spf13/pflag"
"kubesphere.io/kubesphere/pkg/simple/client/network/ippool"
)
type NSNPOptions struct {
@@ -27,24 +25,20 @@ type NSNPOptions struct {
}
type Options struct {
EnableNetworkPolicy bool `json:"enableNetworkPolicy,omitempty" yaml:"enableNetworkPolicy"`
NSNPOptions NSNPOptions `json:"nsnpOptions,omitempty" yaml:"nsnpOptions,omitempty"`
EnableIPPool bool `json:"enableIPPool,omitempty" yaml:"enableIPPool"`
IPPoolOptions ippool.Options `json:"ippoolOptions,omitempty" yaml:"ippoolOptions,omitempty"`
WeaveScopeHost string `json:"weaveScopeHost,omitempty" yaml:"weaveScopeHost,omitempty"`
EnableNetworkPolicy bool `json:"enableNetworkPolicy,omitempty" yaml:"enableNetworkPolicy"`
NSNPOptions NSNPOptions `json:"nsnpOptions,omitempty" yaml:"nsnpOptions,omitempty"`
WeaveScopeHost string `json:"weaveScopeHost,omitempty" yaml:"weaveScopeHost,omitempty"`
IPPoolType string `json:"ippoolType,omitempty" yaml:"ippoolType,omitempty"`
}
// NewNetworkOptions returns a `zero` instance
func NewNetworkOptions() *Options {
return &Options{
EnableNetworkPolicy: false,
EnableIPPool: false,
IPPoolType: "none",
NSNPOptions: NSNPOptions{
AllowedIngressNamespaces: []string{},
},
IPPoolOptions: ippool.Options{
Calico: nil,
},
WeaveScopeHost: "",
}
}
@@ -56,16 +50,15 @@ func (s *Options) Validate() []error {
func (s *Options) ApplyTo(options *Options) {
options.EnableNetworkPolicy = s.EnableNetworkPolicy
options.EnableIPPool = s.EnableIPPool
options.IPPoolType = s.IPPoolType
options.NSNPOptions = s.NSNPOptions
options.IPPoolOptions = s.IPPoolOptions
options.WeaveScopeHost = s.WeaveScopeHost
}
func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) {
fs.BoolVar(&s.EnableNetworkPolicy, "enable-network-policy", c.EnableNetworkPolicy,
"This field instructs KubeSphere to enable network policy or not.")
fs.BoolVar(&s.EnableIPPool, "enable-ippool", c.EnableIPPool,
fs.StringVar(&s.IPPoolType, "ippool-type", c.IPPoolType,
"This field instructs KubeSphere to enable ippool or not.")
fs.StringVar(&s.WeaveScopeHost, "weave-scope-host", c.WeaveScopeHost,
"Weave Scope service endpoint which build a topology API of the applications and the containers running on the hosts")

View File

@@ -96,6 +96,10 @@ func main() {
mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindNamespaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularNamespaceNetworkPolicy), meta.RESTScopeRoot)
mapper.AddSpecific(networkv1alpha1.SchemeGroupVersion.WithKind(networkv1alpha1.ResourceKindIPPool),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralIPPool),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourceSingularIPPool), meta.RESTScopeRoot)
mapper.AddSpecific(devopsv1alpha3.SchemeGroupVersion.WithKind(devopsv1alpha3.ResourceKindDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourceSingularDevOpsProject), meta.RESTScopeRoot)
@@ -147,6 +151,7 @@ func main() {
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilderTemplate),
devopsv1alpha1.SchemeGroupVersion.WithResource(devopsv1alpha1.ResourcePluralS2iBuilder),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralNamespaceNetworkPolicy),
networkv1alpha1.SchemeGroupVersion.WithResource(networkv1alpha1.ResourcePluralIPPool),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject),
devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralPipeline),
clusterv1alpha1.SchemeGroupVersion.WithResource(clusterv1alpha1.ResourcesPluralCluster),