feat: upgrade gateway with lagaccy configmap configs
Signed-off-by: Roland.Ma <rolandma@kubesphere.io>
This commit is contained in:
@@ -40,10 +40,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SidecarInject = "sidecar.istio.io/inject"
|
||||
gatewayPrefix = "kubesphere-router-"
|
||||
workingNamespace = "kubesphere-controls-system"
|
||||
globalGatewayname = gatewayPrefix + "kubesphere-system"
|
||||
helmPatch = `{"metadata":{"annotations":{"meta.helm.sh/release-name":"%s-ingress","meta.helm.sh/release-namespace":"%s"},"labels":{"helm.sh/chart":"ingress-nginx-3.35.0","app.kubernetes.io/managed-by":"Helm","app":null,"component":null,"tier":null}}}`
|
||||
helmPatch = `{"metadata":{"annotations":{"meta.helm.sh/release-name":"%s-ingress","meta.helm.sh/release-namespace":"%s"},"labels":{"helm.sh/chart":"ingress-nginx-3.35.0","app.kubernetes.io/managed-by":"Helm","app":null,"component":null,"tier":null}},"spec":{"selector":null}}`
|
||||
)
|
||||
|
||||
type GatewayOperator interface {
|
||||
@@ -121,12 +122,15 @@ func (c *gatewayOperator) getLegacyGateway(namespace string) *v1alpha1.Gateway {
|
||||
|
||||
// create a fake Gateway object when legacy service exists
|
||||
if len(s.Items) > 0 {
|
||||
return c.convert(namespace, &s.Items[0])
|
||||
d := &appsv1.Deployment{}
|
||||
c.client.Get(context.TODO(), client.ObjectKeyFromObject(&s.Items[0]), d)
|
||||
|
||||
return c.convert(namespace, &s.Items[0], d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) convert(namespace string, svc *corev1.Service) *v1alpha1.Gateway {
|
||||
func (c *gatewayOperator) convert(namespace string, svc *corev1.Service, deploy *appsv1.Deployment) *v1alpha1.Gateway {
|
||||
legacy := v1alpha1.Gateway{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "",
|
||||
@@ -147,8 +151,15 @@ func (c *gatewayOperator) convert(namespace string, svc *corev1.Service) *v1alph
|
||||
Annotations: svc.Annotations,
|
||||
Type: svc.Spec.Type,
|
||||
},
|
||||
Deployment: v1alpha1.DeploymentSpec{
|
||||
Replicas: deploy.Spec.Replicas,
|
||||
},
|
||||
},
|
||||
}
|
||||
if an, ok := deploy.Annotations[SidecarInject]; ok {
|
||||
legacy.Spec.Deployment.Annotations = make(map[string]string)
|
||||
legacy.Spec.Deployment.Annotations[SidecarInject] = an
|
||||
}
|
||||
return &legacy
|
||||
}
|
||||
|
||||
@@ -228,14 +239,25 @@ func (c *gatewayOperator) UpgradeGateway(namespace string) (*v1alpha1.Gateway, e
|
||||
return nil, fmt.Errorf("invalid operation, can't upgrade legacy gateway when working namespace changed")
|
||||
}
|
||||
|
||||
// Get legency gateway's config from configmap
|
||||
cm := &corev1.ConfigMap{}
|
||||
err := c.client.Get(context.TODO(), client.ObjectKey{Namespace: l.Namespace, Name: fmt.Sprintf("%s-nginx", l.Name)}, cm)
|
||||
if err == nil {
|
||||
l.Spec.Conroller.Config = cm.Data
|
||||
defer func() {
|
||||
c.client.Delete(context.TODO(), cm)
|
||||
}()
|
||||
}
|
||||
|
||||
// Delete old deployment, because it's not compatile with the deployment in the helm chart.
|
||||
// We can't defer here, there's a potential race condition causing gateway operator fails.
|
||||
d := &appsv1.Deployment{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Namespace: l.Namespace,
|
||||
Name: l.Name,
|
||||
},
|
||||
}
|
||||
err := c.client.Delete(context.TODO(), d)
|
||||
err = c.client.Delete(context.TODO(), d)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
@@ -259,14 +281,14 @@ func (c *gatewayOperator) UpgradeGateway(namespace string) (*v1alpha1.Gateway, e
|
||||
}
|
||||
|
||||
func (c *gatewayOperator) ListGateways(query *query.Query) (*api.ListResult, error) {
|
||||
applications := v1alpha1.GatewayList{}
|
||||
err := c.cache.List(context.TODO(), &applications, &client.ListOptions{LabelSelector: query.Selector()})
|
||||
gateways := v1alpha1.GatewayList{}
|
||||
err := c.cache.List(context.TODO(), &gateways, &client.ListOptions{LabelSelector: query.Selector()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []runtime.Object
|
||||
for i := range applications.Items {
|
||||
result = append(result, &applications.Items[i])
|
||||
for i := range gateways.Items {
|
||||
result = append(result, &gateways.Items[i])
|
||||
}
|
||||
|
||||
services := &corev1.ServiceList{}
|
||||
@@ -282,29 +304,40 @@ func (c *gatewayOperator) ListGateways(query *query.Query) (*api.ListResult, err
|
||||
})
|
||||
|
||||
for _, s := range services.Items {
|
||||
g := c.convert(s.Labels["project"], &s)
|
||||
result = append(result, g)
|
||||
result = append(result, &s)
|
||||
}
|
||||
|
||||
return v1alpha3.DefaultList(result, query, c.compare, c.filter), nil
|
||||
return v1alpha3.DefaultList(result, query, c.compare, c.filter, c.transform), nil
|
||||
}
|
||||
|
||||
func (d *gatewayOperator) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
|
||||
func (c *gatewayOperator) transform(obj runtime.Object) runtime.Object {
|
||||
if g, ok := obj.(*v1alpha1.Gateway); ok {
|
||||
return g
|
||||
}
|
||||
if s, ok := obj.(*corev1.Service); ok {
|
||||
d := &appsv1.Deployment{}
|
||||
c.client.Get(context.TODO(), client.ObjectKeyFromObject(s), d)
|
||||
return c.convert(s.Labels["project"], s, d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
leftApplication, ok := left.(*v1alpha1.Gateway)
|
||||
func (c *gatewayOperator) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
|
||||
|
||||
leftGateway, ok := left.(*v1alpha1.Gateway)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
rightApplication, ok := right.(*v1alpha1.Gateway)
|
||||
rightGateway, ok := right.(*v1alpha1.Gateway)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return v1alpha3.DefaultObjectMetaCompare(leftApplication.ObjectMeta, rightApplication.ObjectMeta, field)
|
||||
return v1alpha3.DefaultObjectMetaCompare(leftGateway.ObjectMeta, rightGateway.ObjectMeta, field)
|
||||
}
|
||||
|
||||
func (d *gatewayOperator) filter(object runtime.Object, filter query.Filter) bool {
|
||||
func (c *gatewayOperator) filter(object runtime.Object, filter query.Filter) bool {
|
||||
gateway, ok := object.(*v1alpha1.Gateway)
|
||||
if !ok {
|
||||
return false
|
||||
|
||||
@@ -26,12 +26,15 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"kubesphere.io/api/gateway/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
|
||||
"sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
|
||||
)
|
||||
|
||||
func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
@@ -239,6 +242,45 @@ func create_LegacyGateway(c client.Client, namespace string) {
|
||||
},
|
||||
}
|
||||
c.Create(context.TODO(), s)
|
||||
|
||||
d := &appsv1.Deployment{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: fmt.Sprint(gatewayPrefix, namespace),
|
||||
Namespace: workingNamespace,
|
||||
Annotations: map[string]string{
|
||||
SidecarInject: "true",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"app": "kubesphere",
|
||||
"component": "ks-router",
|
||||
"tier": "backend",
|
||||
"project": namespace,
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &[]int32{1}[0],
|
||||
},
|
||||
}
|
||||
c.Create(context.TODO(), d)
|
||||
}
|
||||
|
||||
func create_LegacyGatewayConfigMap(c client.Client, namespace string) {
|
||||
s := &corev1.ConfigMap{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: fmt.Sprint(gatewayPrefix, namespace, "-nginx"),
|
||||
Namespace: workingNamespace,
|
||||
Labels: map[string]string{
|
||||
"app": "kubesphere",
|
||||
"component": "ks-router",
|
||||
"tier": "backend",
|
||||
"project": namespace,
|
||||
},
|
||||
},
|
||||
Data: map[string]string{
|
||||
"fake": "true",
|
||||
},
|
||||
}
|
||||
c.Create(context.TODO(), s)
|
||||
}
|
||||
|
||||
func Test_gatewayOperator_CreateGateway(t *testing.T) {
|
||||
@@ -508,6 +550,7 @@ func Test_gatewayOperator_UpgradeGateway(t *testing.T) {
|
||||
appsv1.AddToScheme(Scheme)
|
||||
client2 := fake.NewFakeClientWithScheme(Scheme)
|
||||
create_LegacyGateway(client2, "project2")
|
||||
create_LegacyGatewayConfigMap(client2, "project2")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -552,6 +595,9 @@ func Test_gatewayOperator_UpgradeGateway(t *testing.T) {
|
||||
Enabled: true,
|
||||
Namespace: "project2",
|
||||
},
|
||||
Config: map[string]string{
|
||||
"fake": "true",
|
||||
},
|
||||
},
|
||||
Service: v1alpha1.ServiceSpec{
|
||||
Annotations: map[string]string{
|
||||
@@ -559,6 +605,12 @@ func Test_gatewayOperator_UpgradeGateway(t *testing.T) {
|
||||
},
|
||||
Type: corev1.ServiceTypeNodePort,
|
||||
},
|
||||
Deployment: v1alpha1.DeploymentSpec{
|
||||
Replicas: &[]int32{1}[0],
|
||||
Annotations: map[string]string{
|
||||
"sidecar.istio.io/inject": "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -580,3 +632,160 @@ func Test_gatewayOperator_UpgradeGateway(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_gatewayOperator_ListGateways(t *testing.T) {
|
||||
type fields struct {
|
||||
client client.Client
|
||||
cache cache.Cache
|
||||
options *gateway.Options
|
||||
}
|
||||
type args struct {
|
||||
query *query.Query
|
||||
}
|
||||
|
||||
var Scheme = runtime.NewScheme()
|
||||
v1alpha1.AddToScheme(Scheme)
|
||||
corev1.AddToScheme(Scheme)
|
||||
appsv1.AddToScheme(Scheme)
|
||||
|
||||
client := fake.NewFakeClientWithScheme(Scheme)
|
||||
|
||||
create_LegacyGateway(client, "project2")
|
||||
|
||||
client.Create(context.TODO(), &v1alpha1.Gateway{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "kubesphere-router-project1",
|
||||
Namespace: "project1",
|
||||
},
|
||||
})
|
||||
|
||||
gates := []*v1alpha1.Gateway{
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: fmt.Sprint(gatewayPrefix, "project2"),
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
Spec: v1alpha1.GatewaySpec{
|
||||
Conroller: v1alpha1.ControllerSpec{
|
||||
Scope: v1alpha1.Scope{
|
||||
Enabled: true,
|
||||
Namespace: "project2",
|
||||
},
|
||||
},
|
||||
Service: v1alpha1.ServiceSpec{
|
||||
Annotations: map[string]string{
|
||||
"fake": "true",
|
||||
},
|
||||
Type: corev1.ServiceTypeNodePort,
|
||||
},
|
||||
Deployment: v1alpha1.DeploymentSpec{
|
||||
Replicas: &[]int32{1}[0],
|
||||
Annotations: map[string]string{
|
||||
SidecarInject: "true",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: fmt.Sprint(gatewayPrefix, "project1"),
|
||||
Namespace: "project1",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
items := make([]interface{}, 0)
|
||||
for _, obj := range gates {
|
||||
items = append(items, obj)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *api.ListResult
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list all gateways",
|
||||
fields: fields{
|
||||
client: client,
|
||||
cache: &fakeClient{Client: client},
|
||||
options: &gateway.Options{
|
||||
Namespace: "kubesphere-controls-system",
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
query: &query.Query{},
|
||||
},
|
||||
want: &api.ListResult{
|
||||
TotalItems: 2,
|
||||
Items: items,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &gatewayOperator{
|
||||
client: tt.fields.client,
|
||||
cache: tt.fields.cache,
|
||||
options: tt.fields.options,
|
||||
}
|
||||
got, err := c.ListGateways(tt.args.query)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("gatewayOperator.ListGateways() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("gatewayOperator.ListGateways() has wrong object\nDiff:\n %s", diff.ObjectGoPrintSideBySide(tt.want, got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
Client client.Client
|
||||
}
|
||||
|
||||
// Get retrieves an obj for the given object key from the Kubernetes Cluster.
|
||||
// obj must be a struct pointer so that obj can be updated with the response
|
||||
// returned by the Server.
|
||||
func (f *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object) error {
|
||||
return f.Client.Get(ctx, key, obj)
|
||||
}
|
||||
|
||||
// List retrieves list of objects for a given namespace and list options. On a
|
||||
// successful call, Items field in the list will be populated with the
|
||||
// result returned from the server.
|
||||
func (f *fakeClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
|
||||
return f.Client.List(ctx, list, opts...)
|
||||
}
|
||||
|
||||
// GetInformer fetches or constructs an informer for the given object that corresponds to a single
|
||||
// API kind and resource.
|
||||
func (f *fakeClient) GetInformer(ctx context.Context, obj client.Object) (cache.Informer, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetInformerForKind is similar to GetInformer, except that it takes a group-version-kind, instead
|
||||
// of the underlying object.
|
||||
func (f *fakeClient) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (cache.Informer, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Start runs all the informers known to this cache until the context is closed.
|
||||
// It blocks.
|
||||
func (f *fakeClient) Start(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForCacheSync waits for all the caches to sync. Returns false if it could not sync a cache.
|
||||
func (f *fakeClient) WaitForCacheSync(ctx context.Context) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *fakeClient) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user