fix: reconcile ingress/route when annotations changed (#862)
* fix: reconcile ingress/route when annotations changes Signed-off-by: Anatolii Bazko <abazko@redhat.com>pull/869/head
parent
af0d7c83b7
commit
0b4fef03e8
|
|
@ -105,14 +105,15 @@ const (
|
|||
OAuthScmConfiguration = "oauth-scm-configuration"
|
||||
|
||||
// che.eclipse.org annotations
|
||||
CheEclipseOrgMountPath = "che.eclipse.org/mount-path"
|
||||
CheEclipseOrgMountAs = "che.eclipse.org/mount-as"
|
||||
CheEclipseOrgEnvName = "che.eclipse.org/env-name"
|
||||
CheEclipseOrgNamespace = "che.eclipse.org/namespace"
|
||||
CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials"
|
||||
CheEclipseOrgOAuthScmServer = "che.eclipse.org/oauth-scm-server"
|
||||
CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint"
|
||||
CheEclipseOrgHash256 = "che.eclipse.org/hash256"
|
||||
CheEclipseOrgMountPath = "che.eclipse.org/mount-path"
|
||||
CheEclipseOrgMountAs = "che.eclipse.org/mount-as"
|
||||
CheEclipseOrgEnvName = "che.eclipse.org/env-name"
|
||||
CheEclipseOrgNamespace = "che.eclipse.org/namespace"
|
||||
CheEclipseOrgGithubOAuthCredentials = "che.eclipse.org/github-oauth-credentials"
|
||||
CheEclipseOrgOAuthScmServer = "che.eclipse.org/oauth-scm-server"
|
||||
CheEclipseOrgScmServerEndpoint = "che.eclipse.org/scm-server-endpoint"
|
||||
CheEclipseOrgHash256 = "che.eclipse.org/hash256"
|
||||
CheEclipseOrgManagedAnnotationsDigest = "che.eclipse.org/managed-annotations-digest"
|
||||
|
||||
// components
|
||||
IdentityProviderName = "keycloak"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/deploy"
|
||||
|
|
@ -33,6 +34,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -474,18 +476,19 @@ func isOnlyOneOperatorManagesDWResources(deployContext *deploy.DeployContext) (b
|
|||
func readK8SObject(yamlFile string, obj interface{}) (*Object2Sync, error) {
|
||||
_, exists := cachedObj[yamlFile]
|
||||
if !exists {
|
||||
if err := util.ReadObject(yamlFile, obj); err != nil {
|
||||
data, err := ioutil.ReadFile(yamlFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash256, err := util.ComputeHash256(yamlFile)
|
||||
err = yaml.Unmarshal(data, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cachedObj[yamlFile] = &Object2Sync{
|
||||
obj.(metav1.Object),
|
||||
hash256,
|
||||
util.ComputeHash256(data),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ package deploy
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
|
@ -24,10 +23,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
|
@ -41,7 +36,6 @@ func TestIngressSpec(t *testing.T) {
|
|||
ingressComponent string
|
||||
serviceName string
|
||||
servicePort int
|
||||
initObjects []runtime.Object
|
||||
ingressCustomSettings orgv1.IngressCustomSettings
|
||||
expectedIngress *v1beta1.Ingress
|
||||
}
|
||||
|
|
@ -49,6 +43,7 @@ func TestIngressSpec(t *testing.T) {
|
|||
cheCluster := &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +60,6 @@ func TestIngressSpec(t *testing.T) {
|
|||
Labels: "type=default",
|
||||
Annotations: map[string]string{"annotation-key": "annotation-value"},
|
||||
},
|
||||
initObjects: []runtime.Object{},
|
||||
expectedIngress: &v1beta1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
|
|
@ -78,6 +72,7 @@ func TestIngressSpec(t *testing.T) {
|
|||
"app.kubernetes.io/name": DefaultCheFlavor(cheCluster),
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"che.eclipse.org/managed-annotations-digest": "0000",
|
||||
"kubernetes.io/ingress.class": "nginx",
|
||||
"nginx.ingress.kubernetes.io/proxy-connect-timeout": "3600",
|
||||
"nginx.ingress.kubernetes.io/proxy-read-timeout": "3600",
|
||||
|
|
@ -115,18 +110,7 @@ func TestIngressSpec(t *testing.T) {
|
|||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
testCase.initObjects = append(testCase.initObjects)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
|
||||
|
||||
deployContext := &DeployContext{
|
||||
CheCluster: cheCluster,
|
||||
ClusterAPI: ClusterAPI{
|
||||
Client: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
deployContext := GetTestDeployContext(cheCluster, []runtime.Object{})
|
||||
|
||||
_, actualIngress := GetIngressSpec(deployContext,
|
||||
testCase.ingressName,
|
||||
|
|
@ -146,21 +130,7 @@ func TestIngressSpec(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSyncIngressToCluster(t *testing.T) {
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
deployContext := &DeployContext{
|
||||
CheCluster: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
ClusterAPI: ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachedClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
deployContext := GetTestDeployContext(nil, []runtime.Object{})
|
||||
|
||||
_, done, err := SyncIngressToCluster(deployContext, "test", "host-1", "", "service-1", 8080, orgv1.IngressCustomSettings{}, "component")
|
||||
if !done || err != nil {
|
||||
|
|
@ -173,7 +143,7 @@ func TestSyncIngressToCluster(t *testing.T) {
|
|||
}
|
||||
|
||||
actual := &v1beta1.Ingress{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get ingress: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ package deploy
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
|
||||
|
|
@ -27,7 +28,8 @@ import (
|
|||
var ingressDiffOpts = cmp.Options{
|
||||
cmpopts.IgnoreFields(v1beta1.Ingress{}, "TypeMeta", "Status"),
|
||||
cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
|
||||
return reflect.DeepEqual(x.Labels, y.Labels)
|
||||
return reflect.DeepEqual(x.Labels, y.Labels) &&
|
||||
x.Annotations[CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[CheEclipseOrgManagedAnnotationsDigest]
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
@ -101,12 +103,30 @@ func GetIngressSpec(
|
|||
if ingressStrategy != "multi-host" && (component == DevfileRegistryName || component == PluginRegistryName) {
|
||||
annotations["nginx.ingress.kubernetes.io/rewrite-target"] = "/$1"
|
||||
}
|
||||
|
||||
// add custom annotations
|
||||
for k, v := range ingressCustomSettings.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
|
||||
// add 'che.eclipse.org/managed-annotations-digest' annotation
|
||||
// to store and compare annotations managed by operator only
|
||||
annotationsKeys := make([]string, 0, len(annotations))
|
||||
for k := range annotations {
|
||||
annotationsKeys = append(annotationsKeys, k)
|
||||
}
|
||||
if len(annotationsKeys) > 0 {
|
||||
sort.Strings(annotationsKeys)
|
||||
|
||||
data := ""
|
||||
for _, k := range annotationsKeys {
|
||||
data += k + ":" + annotations[k] + ","
|
||||
}
|
||||
if util.IsTestMode() {
|
||||
annotations[CheEclipseOrgManagedAnnotationsDigest] = "0000"
|
||||
} else {
|
||||
annotations[CheEclipseOrgManagedAnnotationsDigest] = util.ComputeHash256([]byte(data))
|
||||
}
|
||||
}
|
||||
|
||||
ingress := &v1beta1.Ingress{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Ingress",
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
orgv1 "github.com/eclipse-che/che-operator/pkg/apis/org/v1"
|
||||
"github.com/eclipse-che/che-operator/pkg/util"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
|
|
@ -35,14 +37,16 @@ var routeDiffOpts = cmp.Options{
|
|||
cmpopts.IgnoreFields(routev1.Route{}, "TypeMeta", "Status"),
|
||||
cmpopts.IgnoreFields(routev1.RouteSpec{}, "Host", "WildcardPolicy"),
|
||||
cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
|
||||
return reflect.DeepEqual(x.Labels, y.Labels)
|
||||
return reflect.DeepEqual(x.Labels, y.Labels) &&
|
||||
x.Annotations[CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[CheEclipseOrgManagedAnnotationsDigest]
|
||||
}),
|
||||
}
|
||||
var routeWithHostDiffOpts = cmp.Options{
|
||||
cmpopts.IgnoreFields(routev1.Route{}, "TypeMeta", "Status"),
|
||||
cmpopts.IgnoreFields(routev1.RouteSpec{}, "WildcardPolicy"),
|
||||
cmp.Comparer(func(x, y metav1.ObjectMeta) bool {
|
||||
return reflect.DeepEqual(x.Labels, y.Labels)
|
||||
return reflect.DeepEqual(x.Labels, y.Labels) &&
|
||||
x.Annotations[CheEclipseOrgManagedAnnotationsDigest] == y.Annotations[CheEclipseOrgManagedAnnotationsDigest]
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
@ -84,9 +88,32 @@ func GetRouteSpec(
|
|||
MergeLabels(labels, routeCustomSettings.Labels)
|
||||
|
||||
// add custom annotations
|
||||
annotations := map[string]string{}
|
||||
for k, v := range routeCustomSettings.Annotations {
|
||||
annotations[k] = v
|
||||
var annotations map[string]string
|
||||
if len(routeCustomSettings.Annotations) > 0 {
|
||||
annotations = make(map[string]string)
|
||||
for k, v := range routeCustomSettings.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// add 'che.eclipse.org/managed-annotations-digest' annotation
|
||||
// to store and compare annotations managed by operator only
|
||||
annotationsKeys := make([]string, 0, len(annotations))
|
||||
for k := range annotations {
|
||||
annotationsKeys = append(annotationsKeys, k)
|
||||
}
|
||||
if len(annotationsKeys) > 0 {
|
||||
sort.Strings(annotationsKeys)
|
||||
|
||||
data := ""
|
||||
for _, k := range annotationsKeys {
|
||||
data += k + ":" + annotations[k] + ","
|
||||
}
|
||||
if util.IsTestMode() {
|
||||
annotations[CheEclipseOrgManagedAnnotationsDigest] = "0000"
|
||||
} else {
|
||||
annotations[CheEclipseOrgManagedAnnotationsDigest] = util.ComputeHash256([]byte(data))
|
||||
}
|
||||
}
|
||||
|
||||
weight := int32(100)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ package deploy
|
|||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
|
@ -24,10 +23,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
|
@ -43,7 +38,6 @@ func TestRouteSpec(t *testing.T) {
|
|||
routeComponent string
|
||||
serviceName string
|
||||
servicePort int32
|
||||
initObjects []runtime.Object
|
||||
routeCustomSettings orgv1.RouteCustomSettings
|
||||
expectedRoute *routev1.Route
|
||||
}
|
||||
|
|
@ -51,6 +45,7 @@ func TestRouteSpec(t *testing.T) {
|
|||
cheCluster := &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -62,18 +57,13 @@ func TestRouteSpec(t *testing.T) {
|
|||
serviceName: "che",
|
||||
servicePort: 8080,
|
||||
routeCustomSettings: orgv1.RouteCustomSettings{
|
||||
Labels: "type=default",
|
||||
Domain: "route-domain",
|
||||
Annotations: map[string]string{"annotation-key": "annotation-value"},
|
||||
Labels: "type=default",
|
||||
Domain: "route-domain",
|
||||
},
|
||||
initObjects: []runtime.Object{},
|
||||
expectedRoute: &routev1.Route{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "eclipse-che",
|
||||
Annotations: map[string]string{
|
||||
"annotation-key": "annotation-value",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
"type": "default",
|
||||
"app.kubernetes.io/component": "test-component",
|
||||
|
|
@ -110,10 +100,8 @@ func TestRouteSpec(t *testing.T) {
|
|||
serviceName: "che",
|
||||
servicePort: 8080,
|
||||
routeCustomSettings: orgv1.RouteCustomSettings{
|
||||
Labels: "type=default",
|
||||
Annotations: map[string]string{"annotation-key": "annotation-value"},
|
||||
Labels: "type=default",
|
||||
},
|
||||
initObjects: []runtime.Object{},
|
||||
expectedRoute: &routev1.Route{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
|
|
@ -125,9 +113,6 @@ func TestRouteSpec(t *testing.T) {
|
|||
"app.kubernetes.io/managed-by": DefaultCheFlavor(cheCluster) + "-operator",
|
||||
"app.kubernetes.io/name": DefaultCheFlavor(cheCluster),
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"annotation-key": "annotation-value",
|
||||
},
|
||||
},
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Route",
|
||||
|
|
@ -153,18 +138,7 @@ func TestRouteSpec(t *testing.T) {
|
|||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
logf.SetLogger(zap.LoggerTo(os.Stdout, true))
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
testCase.initObjects = append(testCase.initObjects)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme, testCase.initObjects...)
|
||||
|
||||
deployContext := &DeployContext{
|
||||
CheCluster: cheCluster,
|
||||
ClusterAPI: ClusterAPI{
|
||||
Client: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
deployContext := GetTestDeployContext(cheCluster, []runtime.Object{})
|
||||
|
||||
actualRoute, err := GetRouteSpec(deployContext,
|
||||
testCase.routeName,
|
||||
|
|
@ -187,22 +161,9 @@ func TestRouteSpec(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSyncRouteToCluster(t *testing.T) {
|
||||
orgv1.SchemeBuilder.AddToScheme(scheme.Scheme)
|
||||
routev1.AddToScheme(scheme.Scheme)
|
||||
cli := fake.NewFakeClientWithScheme(scheme.Scheme)
|
||||
deployContext := &DeployContext{
|
||||
CheCluster: &orgv1.CheCluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "eclipse-che",
|
||||
Name: "eclipse-che",
|
||||
},
|
||||
},
|
||||
ClusterAPI: ClusterAPI{
|
||||
Client: cli,
|
||||
NonCachedClient: cli,
|
||||
Scheme: scheme.Scheme,
|
||||
},
|
||||
}
|
||||
// init context
|
||||
deployContext := GetTestDeployContext(nil, []runtime.Object{})
|
||||
routev1.AddToScheme(deployContext.ClusterAPI.Scheme)
|
||||
|
||||
done, err := SyncRouteToCluster(deployContext, "test", "", "", "service", 80, orgv1.RouteCustomSettings{}, "test")
|
||||
if !done || err != nil {
|
||||
|
|
@ -216,7 +177,7 @@ func TestSyncRouteToCluster(t *testing.T) {
|
|||
}
|
||||
|
||||
actual := &routev1.Route{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get route: %v", err)
|
||||
}
|
||||
|
|
@ -231,7 +192,7 @@ func TestSyncRouteToCluster(t *testing.T) {
|
|||
}
|
||||
|
||||
actual = &routev1.Route{}
|
||||
err = cli.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get route: %v", err)
|
||||
}
|
||||
|
|
@ -241,4 +202,16 @@ func TestSyncRouteToCluster(t *testing.T) {
|
|||
if actual.Spec.Host != "test-eclipse-che.domain" {
|
||||
t.Fatalf("Failed to sync route")
|
||||
}
|
||||
|
||||
// sync route with annotations
|
||||
done, err = SyncRouteToCluster(deployContext, "test", "", "", "service", 90, orgv1.RouteCustomSettings{Annotations: map[string]string{"a": "b"}}, "test")
|
||||
|
||||
actual = &routev1.Route{}
|
||||
err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Name: "test"}, actual)
|
||||
if !done || err != nil {
|
||||
t.Fatalf("Failed to sync route: %v", err)
|
||||
}
|
||||
if actual.ObjectMeta.Annotations["a"] != "b" || actual.ObjectMeta.Annotations[CheEclipseOrgManagedAnnotationsDigest] == "" {
|
||||
t.Fatalf("Failed to sync route")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,14 +431,8 @@ func ReadObject(yamlFile string, obj interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func ComputeHash256(yamlFile string) (string, error) {
|
||||
data, err := ioutil.ReadFile(yamlFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func ComputeHash256(data []byte) string {
|
||||
hasher := sha256.New()
|
||||
hasher.Write(data)
|
||||
sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
return sha, nil
|
||||
return base64.URLEncoding.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue