add controllers
change kiali mux to go-restful add knative
This commit is contained in:
42
pkg/controller/strategy/helper.go
Normal file
42
pkg/controller/strategy/helper.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package strategy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"k8s.io/api/core/v1"
|
||||
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
)
|
||||
|
||||
const (
|
||||
AppLabel = "app"
|
||||
)
|
||||
|
||||
func getAppNameByStrategy(strategy *v1alpha2.Strategy) string {
|
||||
if len(strategy.Labels) > 0 && len(strategy.Labels[AppLabel]) > 0 {
|
||||
return strategy.Labels[AppLabel]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func fillDestinationPort(vs *v1alpha3.VirtualService, service *v1.Service) error {
|
||||
|
||||
if len(service.Spec.Ports) == 0 {
|
||||
return fmt.Errorf("service %s/%s spec doesn't canotain any ports", service.Namespace, service.Name)
|
||||
}
|
||||
|
||||
// fill http port
|
||||
for i := range vs.Spec.Http {
|
||||
for j := range vs.Spec.Http[i].Route {
|
||||
vs.Spec.Http[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
|
||||
}
|
||||
}
|
||||
|
||||
// fill tcp port
|
||||
for i := range vs.Spec.Tcp {
|
||||
for j := range vs.Spec.Tcp[i].Route {
|
||||
vs.Spec.Tcp[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -18,17 +18,17 @@ package strategy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
servicemeshv1alpha2 "kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
"reflect"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var log = logf.Log.WithName("controller")
|
||||
var log = logf.Log.WithName("strategy-controller")
|
||||
|
||||
// Add creates a new Strategy Controller and adds it to the Manager with default RBAC. The Manager will set fields on the Controller
|
||||
// and Start it when the Manager is Started.
|
||||
@@ -62,10 +62,6 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.Watch(&source.Kind{Type: &v1alpha3.VirtualService{}}, &handler.EnqueueRequestForObject{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(user): Modify this to be the types you create
|
||||
// Watch a VirtualService created by Strategy
|
||||
@@ -97,32 +93,33 @@ type ReconcileStrategy struct {
|
||||
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=servicemesh.kubesphere.io,resources=strategies/status,verbs=get;update;patch
|
||||
func (r *ReconcileStrategy) Reconcile(request reconcile.Request) (reconcile.Result, error) {
|
||||
|
||||
// Fetch the Strategy instance
|
||||
strategy := &servicemeshv1alpha2.Strategy{}
|
||||
err := r.Get(context.TODO(), request.NamespacedName, strategy)
|
||||
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
// Object not found, return. Created objects are automatically garbage collected.
|
||||
// For additional cleanup logic use finalizers.
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
// Error reading the object - requeue the request.
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Define VirtualService to be created
|
||||
vs := &v1alpha3.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: strategy.Name + "-virtualservice",
|
||||
Namespace: strategy.Namespace,
|
||||
Labels: strategy.Spec.Selector.MatchLabels,
|
||||
},
|
||||
Spec: strategy.Spec.Template.Spec,
|
||||
return r.reconcileStrategy(strategy)
|
||||
}
|
||||
|
||||
func (r *ReconcileStrategy) reconcileStrategy(strategy *servicemeshv1alpha2.Strategy) (reconcile.Result, error) {
|
||||
|
||||
appName := getAppNameByStrategy(strategy)
|
||||
service := &v1.Service{}
|
||||
|
||||
err := r.Get(context.TODO(), types.NamespacedName{Namespace: strategy.Namespace, Name: appName}, service)
|
||||
if err != nil {
|
||||
log.Error(err, "couldn't find service %s/%s,", strategy.Namespace, appName)
|
||||
return reconcile.Result{}, errors.NewBadRequest(fmt.Sprintf("service %s not found", appName))
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(strategy, vs, r.scheme); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
vs, err := r.generateVirtualService(strategy, service)
|
||||
|
||||
// Check if the VirtualService already exists
|
||||
found := &v1alpha3.VirtualService{}
|
||||
@@ -130,14 +127,16 @@ func (r *ReconcileStrategy) Reconcile(request reconcile.Request) (reconcile.Resu
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
log.Info("Creating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
|
||||
err = r.Create(context.TODO(), vs)
|
||||
|
||||
return reconcile.Result{}, err
|
||||
} else if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
// Update the found object and write the result back if there are any changes
|
||||
if !reflect.DeepEqual(vs.Spec, found.Spec) {
|
||||
if !reflect.DeepEqual(vs.Spec, found.Spec) || len(vs.OwnerReferences) == 0 {
|
||||
found.Spec = vs.Spec
|
||||
found.OwnerReferences = vs.OwnerReferences
|
||||
log.Info("Updating VirtualService", "namespace", vs.Namespace, "name", vs.Name)
|
||||
err = r.Update(context.TODO(), found)
|
||||
if err != nil {
|
||||
@@ -146,3 +145,48 @@ func (r *ReconcileStrategy) Reconcile(request reconcile.Request) (reconcile.Resu
|
||||
}
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *ReconcileStrategy) generateVirtualService(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) (*v1alpha3.VirtualService, error) {
|
||||
|
||||
// Define VirtualService to be created
|
||||
vs := &v1alpha3.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: getAppNameByStrategy(strategy),
|
||||
Namespace: strategy.Namespace,
|
||||
Labels: strategy.Spec.Selector.MatchLabels,
|
||||
},
|
||||
Spec: strategy.Spec.Template.Spec,
|
||||
}
|
||||
|
||||
// one version rules them all
|
||||
if len(strategy.Spec.GovernorVersion) > 0 {
|
||||
|
||||
governorDestinationWeight := v1alpha3.DestinationWeight{
|
||||
Destination: v1alpha3.Destination{
|
||||
Host: getAppNameByStrategy(strategy),
|
||||
Subset: strategy.Spec.GovernorVersion,
|
||||
},
|
||||
Weight: 100,
|
||||
}
|
||||
|
||||
if len(strategy.Spec.Template.Spec.Http) > 0 {
|
||||
governorRoute := v1alpha3.HTTPRoute{
|
||||
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
|
||||
}
|
||||
|
||||
vs.Spec.Http = []v1alpha3.HTTPRoute{governorRoute}
|
||||
} else if len(strategy.Spec.Template.Spec.Tcp) > 0 {
|
||||
governorRoute := v1alpha3.TCPRoute{
|
||||
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
|
||||
}
|
||||
vs.Spec.Tcp = []v1alpha3.TCPRoute{governorRoute}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := fillDestinationPort(vs, service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package strategy
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/common/v1alpha1"
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -37,23 +38,47 @@ import (
|
||||
var c client.Client
|
||||
|
||||
var expectedRequest = reconcile.Request{NamespacedName: types.NamespacedName{Name: "foo", Namespace: "default"}}
|
||||
var depKey = types.NamespacedName{Name: "foo-virtualservice", Namespace: "default"}
|
||||
var depKey = types.NamespacedName{Name: "details", Namespace: "default"}
|
||||
|
||||
const timeout = time.Second * 5
|
||||
|
||||
var labels = map[string]string{
|
||||
"app.kubernetes.io/name": "details",
|
||||
"app.kubernetes.io/version": "v1",
|
||||
"app": "details",
|
||||
"servicemesh.kubesphere.io/enabled": "",
|
||||
}
|
||||
|
||||
var svc = v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "details",
|
||||
Namespace: "default",
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 8080,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Selector: labels,
|
||||
},
|
||||
}
|
||||
|
||||
func TestReconcile(t *testing.T) {
|
||||
g := gomega.NewGomegaWithT(t)
|
||||
instance := &servicemeshv1alpha2.Strategy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: "default",
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: servicemeshv1alpha2.StrategySpec{
|
||||
Type: servicemeshv1alpha2.CanaryType,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"type": "Canary",
|
||||
},
|
||||
MatchLabels: labels,
|
||||
},
|
||||
Template: servicemeshv1alpha2.VirtualServiceTemplateSpec{
|
||||
Spec: v1alpha3.VirtualServiceSpec{
|
||||
@@ -111,6 +136,14 @@ func TestReconcile(t *testing.T) {
|
||||
mgrStopped.Wait()
|
||||
}()
|
||||
|
||||
err = c.Create(context.TODO(), &svc)
|
||||
if apierrors.IsInvalid(err) {
|
||||
t.Logf("failed to create service, %v", err)
|
||||
return
|
||||
}
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
//defer c.Delete(context.TODO(), &svc)
|
||||
|
||||
// Create the Strategy object and expect the Reconcile and Deployment to be created
|
||||
err = c.Create(context.TODO(), instance)
|
||||
// The instance object may not be a valid object because it might be missing some required fields.
|
||||
@@ -119,6 +152,7 @@ func TestReconcile(t *testing.T) {
|
||||
t.Logf("failed to create object, got an invalid object error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
defer c.Delete(context.TODO(), instance)
|
||||
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
|
||||
@@ -133,11 +167,11 @@ func TestReconcile(t *testing.T) {
|
||||
|
||||
// Delete the Deployment and expect Reconcile to be called for Deployment deletion
|
||||
g.Expect(c.Delete(context.TODO(), vs)).NotTo(gomega.HaveOccurred())
|
||||
g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
|
||||
//g.Eventually(requests, timeout).Should(gomega.Receive(gomega.Equal(expectedRequest)))
|
||||
//g.Eventually(func() error { return c.Get(context.TODO(), depKey, vs) }, timeout).Should(gomega.Succeed())
|
||||
|
||||
// Manually delete Deployment since GC isn't enabled in the test control plane
|
||||
g.Eventually(func() error { return c.Delete(context.TODO(), vs) }, timeout).
|
||||
Should(gomega.MatchError("virtualservices.networking.istio.io \"foo-virtualservice\" not found"))
|
||||
Should(gomega.MatchError("virtualservices.networking.istio.io \"details\" not found"))
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user