145 lines
4.2 KiB
Go
145 lines
4.2 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"reflect"
|
|
"time"
|
|
|
|
v3 "github.com/projectcalico/libcalico-go/lib/apis/v3"
|
|
"github.com/projectcalico/libcalico-go/lib/clientv3"
|
|
"github.com/projectcalico/libcalico-go/lib/errors"
|
|
"github.com/projectcalico/libcalico-go/lib/options"
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
|
"k8s.io/klog/klogr"
|
|
api "kubesphere.io/kubesphere/pkg/apis/network/v1alpha1"
|
|
)
|
|
|
|
var log = klogr.New().WithName("calico-client")
|
|
var defaultBackoff = wait.Backoff{
|
|
Steps: 4,
|
|
Duration: 10 * time.Millisecond,
|
|
Factor: 5.0,
|
|
Jitter: 0.1,
|
|
}
|
|
|
|
type calicoNetworkProvider struct {
|
|
np clientv3.NetworkPolicyInterface
|
|
}
|
|
|
|
func NewCalicoNetworkProvider(np clientv3.NetworkPolicyInterface) NsNetworkPolicyProvider {
|
|
return &calicoNetworkProvider{
|
|
np: np,
|
|
}
|
|
}
|
|
func convertSpec(n *api.NamespaceNetworkPolicySpec) *v3.NetworkPolicySpec {
|
|
bytes, err := json.Marshal(&n)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
m := new(v3.NetworkPolicySpec)
|
|
err = json.Unmarshal(bytes, m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
// ConvertAPIToCalico convert our api to calico api
|
|
func ConvertAPIToCalico(n *api.NamespaceNetworkPolicy) *v3.NetworkPolicy {
|
|
output := v3.NewNetworkPolicy()
|
|
//Object Metadata
|
|
output.ObjectMeta.Name = n.Name
|
|
output.Namespace = n.Namespace
|
|
output.Annotations = n.Annotations
|
|
output.Labels = n.Labels
|
|
//spec
|
|
output.Spec = *(convertSpec(&n.Spec))
|
|
return output
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) Get(o *api.NamespaceNetworkPolicy) (interface{}, error) {
|
|
return k.np.Get(context.TODO(), o.Namespace, o.Name, options.GetOptions{})
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) Add(o *api.NamespaceNetworkPolicy) error {
|
|
log.V(3).Info("Creating network policy", "name", o.Name, "namespace", o.Namespace)
|
|
obj := ConvertAPIToCalico(o)
|
|
log.V(4).Info("Show object spe detail", "name", o.Name, "namespace", o.Namespace, "Spec", obj.Spec)
|
|
_, err := k.np.Create(context.TODO(), obj, options.SetOptions{})
|
|
return err
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) CheckExist(o *api.NamespaceNetworkPolicy) (bool, error) {
|
|
log.V(3).Info("Checking network policy whether exsits or not", "name", o.Name, "namespace", o.Namespace)
|
|
out, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
|
|
if err != nil {
|
|
if _, ok := err.(errors.ErrorResourceDoesNotExist); ok {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
if out != nil {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) Delete(o *api.NamespaceNetworkPolicy) error {
|
|
log.V(3).Info("Deleting network policy", "name", o.Name, "namespace", o.Namespace)
|
|
_, err := k.np.Delete(context.Background(), o.Namespace, o.Name, options.DeleteOptions{})
|
|
return err
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) NeedUpdate(o *api.NamespaceNetworkPolicy) (bool, error) {
|
|
store, err := k.np.Get(context.Background(), o.Namespace, o.Name, options.GetOptions{})
|
|
if err != nil {
|
|
log.Error(err, "Failed to get resource", "name", o.Name, "namespace", o.Namespace)
|
|
}
|
|
expected := ConvertAPIToCalico(o)
|
|
log.V(4).Info("Comparing Spec", "store", store.Spec, "current", expected.Spec)
|
|
if !reflect.DeepEqual(store.Spec, expected.Spec) {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (k *calicoNetworkProvider) Update(o *api.NamespaceNetworkPolicy) error {
|
|
log.V(3).Info("Updating network policy", "name", o.Name, "namespace", o.Namespace)
|
|
updateObject, err := k.Get(o)
|
|
if err != nil {
|
|
log.Error(err, "Failed to get resource in store")
|
|
return err
|
|
}
|
|
up := updateObject.(*v3.NetworkPolicy)
|
|
up.Spec = *convertSpec(&o.Spec)
|
|
err = RetryOnConflict(defaultBackoff, func() error {
|
|
_, err := k.np.Update(context.Background(), up, options.SetOptions{})
|
|
return err
|
|
})
|
|
if err != nil {
|
|
log.Error(err, "Failed to update resource", "name", o.Name, "namespace", o.Namespace)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// RetryOnConflict is same as the function in k8s, but replaced with error in calico
|
|
func RetryOnConflict(backoff wait.Backoff, fn func() error) error {
|
|
var lastConflictErr error
|
|
err := wait.ExponentialBackoff(backoff, func() (bool, error) {
|
|
err := fn()
|
|
if err == nil {
|
|
return true, nil
|
|
}
|
|
if _, ok := err.(errors.ErrorResourceUpdateConflict); ok {
|
|
lastConflictErr = err
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
})
|
|
if err == wait.ErrWaitTimeout {
|
|
err = lastConflictErr
|
|
}
|
|
return err
|
|
}
|