From 071e51cc45f490cd000276acec8810ed8eef74a6 Mon Sep 17 00:00:00 2001 From: Ryan Emerson Date: Wed, 22 May 2024 18:05:43 +0100 Subject: [PATCH] Operator is using deprecated cache-managers endpoint for health checks with Infinispan 15 Fixes #2094 --- pkg/infinispan/client/api/infinispan.go | 9 ++++ pkg/infinispan/client/client.go | 39 ++++++++++++++-- pkg/infinispan/client/v13/backups_restores.go | 27 +++++------ pkg/infinispan/client/v13/caches.go | 10 ++-- pkg/infinispan/client/v13/container.go | 46 ++++++++----------- pkg/infinispan/client/v13/infinispan_v13.go | 18 +++++--- pkg/infinispan/client/v13/logging.go | 8 ++-- pkg/infinispan/client/v13/path_resolver.go | 31 +++++++++++++ pkg/infinispan/client/v13/server.go | 6 +-- pkg/infinispan/client/v13/xsite.go | 9 ++-- pkg/infinispan/client/v14/caches.go | 4 +- pkg/infinispan/client/v14/infinispan_v14.go | 15 ++++-- pkg/infinispan/client/v15/infinispan_v15.go | 19 ++++++++ pkg/infinispan/client/v15/path_resolver.go | 18 ++++++++ pkg/reconcile/pipeline/infinispan/api.go | 3 ++ .../pipeline/infinispan/api_mocks.go | 15 ++++++ .../pipeline/infinispan/context/context.go | 5 ++ .../infinispan/handler/manage/upgrades.go | 6 ++- .../hotrod_rolling_upgrade_test.go | 24 ++++------ test/e2e/infinispan/authentication_test.go | 4 +- test/e2e/infinispan/authorization_test.go | 2 +- test/e2e/upgrade/upgrade_test.go | 23 +++------- test/e2e/utils/cache.go | 2 +- test/e2e/utils/common.go | 18 +++++++- test/e2e/utils/kubernetes.go | 14 ++++-- test/e2e/utils/olm.go | 17 +++++++ test/e2e/xsite/xsite_test.go | 21 ++++++++- 27 files changed, 297 insertions(+), 116 deletions(-) create mode 100644 pkg/infinispan/client/v13/path_resolver.go create mode 100644 pkg/infinispan/client/v15/infinispan_v15.go create mode 100644 pkg/infinispan/client/v15/path_resolver.go diff --git a/pkg/infinispan/client/api/infinispan.go b/pkg/infinispan/client/api/infinispan.go index 1973f1dab..964db76f3 100644 --- a/pkg/infinispan/client/api/infinispan.go +++ b/pkg/infinispan/client/api/infinispan.go @@ -154,6 +154,7 @@ type BackupRestoreResources struct { type ContainerInfo struct { Coordinator bool `json:"coordinator"` SitesView *[]interface{} `json:"sites_view,omitempty"` + Version string `json:"version"` } type NotSupportedError struct { @@ -163,3 +164,11 @@ type NotSupportedError struct { func (n *NotSupportedError) Error() string { return fmt.Sprintf("Operation not supported with Operand Major Version'%s'", n.Version) } + +type PathResolver interface { + Caches(string) string + CacheManager(string) string + Container(string) string + Logging(string) string + Server(string) string +} diff --git a/pkg/infinispan/client/client.go b/pkg/infinispan/client/client.go index b3d07e082..4acad8263 100644 --- a/pkg/infinispan/client/client.go +++ b/pkg/infinispan/client/client.go @@ -43,19 +43,50 @@ For example, if v14 can reuse a new `api.Cache` implementation but requires a ne package client import ( - "github.com/infinispan/infinispan-operator/pkg/http" + "fmt" + "regexp" + + "github.com/blang/semver" + httpClient "github.com/infinispan/infinispan-operator/pkg/http" "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" v13 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v13" v14 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v14" + v15 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v15" "github.com/infinispan/infinispan-operator/pkg/infinispan/version" ) // New Factory to obtain Infinispan implementation -func New(operand version.Operand, client http.HttpClient) api.Infinispan { - switch operand.UpstreamVersion.Major { +func New(operand version.Operand, client httpClient.HttpClient) api.Infinispan { + return ispnClient(operand.UpstreamVersion.Major, client) +} + +func NewUnknownVersion(client httpClient.HttpClient) (api.Infinispan, error) { + wrapError := func(e error) error { + return fmt.Errorf("unable to determine server version: %w", e) + } + info, err := v15.New(client).Container().Info() + if err != nil { + info, err = v14.New(client).Container().Info() + if err != nil { + return nil, wrapError(err) + } + } + re := regexp.MustCompile(`\d+(\.\d+){2,}`) + versionStr := re.FindStringSubmatch(info.Version) + _version, err := semver.Parse(versionStr[0]) + if err != nil { + return nil, wrapError(fmt.Errorf("unable to parse server version: %w", err)) + } + return ispnClient(_version.Major, client), nil +} + +func ispnClient(majorVersion uint64, client httpClient.HttpClient) api.Infinispan { + switch majorVersion { case 13: return v13.New(client) - default: + case 14: return v14.New(client) + default: + return v15.New(client) } } diff --git a/pkg/infinispan/client/v13/backups_restores.go b/pkg/infinispan/client/v13/backups_restores.go index d919c1ec6..f5764fdc8 100644 --- a/pkg/infinispan/client/v13/backups_restores.go +++ b/pkg/infinispan/client/v13/backups_restores.go @@ -12,39 +12,40 @@ import ( "github.com/infinispan/infinispan-operator/pkg/mime" ) -const ( - BackupPath = CacheManagerPath + "/backups" - RestorePath = CacheManagerPath + "/restores" -) - var validator = inputValidator.New() type backups struct { + api.PathResolver httpClient.HttpClient } type restores struct { + api.PathResolver httpClient.HttpClient } func (b backups) Create(name string, config *api.BackupConfig) (err error) { - url := fmt.Sprintf("%s/%s", BackupPath, name) - return create(url, name, "backup", config, b) + return create(b.backup(name), name, "backup", config, b) } func (b backups) Status(name string) (api.Status, error) { - url := fmt.Sprintf("%s/%s", BackupPath, name) - return status(url, name, "Backup", b) + return status(b.backup(name), name, "Backup", b) } func (r restores) Create(name string, config *api.RestoreConfig) (err error) { - url := fmt.Sprintf("%s/%s", RestorePath, name) - return create(url, name, "restore", config, r) + return create(r.restore(name), name, "restore", config, r) } func (r restores) Status(name string) (api.Status, error) { - url := fmt.Sprintf("%s/%s", RestorePath, name) - return status(url, name, "Restore", r) + return status(r.restore(name), name, "Restore", r) +} + +func (b backups) backup(name string) string { + return b.CacheManager("/backups/" + name) +} + +func (r restores) restore(name string) string { + return r.CacheManager("/restores/" + name) } func create(url, name, op string, config interface{}, client httpClient.HttpClient) (err error) { diff --git a/pkg/infinispan/client/v13/caches.go b/pkg/infinispan/client/v13/caches.go index 67a53bf42..38c0e12f1 100644 --- a/pkg/infinispan/client/v13/caches.go +++ b/pkg/infinispan/client/v13/caches.go @@ -14,19 +14,19 @@ import ( "github.com/infinispan/infinispan-operator/pkg/mime" ) -const CachesPath = BasePath + "/caches" - type cache struct { + api.PathResolver httpClient.HttpClient name string } type caches struct { + api.PathResolver httpClient.HttpClient } func (c *cache) url() string { - return fmt.Sprintf("%s/%s", CachesPath, url.PathEscape(c.name)) + return fmt.Sprintf("%s/%s", c.Caches(""), url.PathEscape(c.name)) } func (c *cache) entryUrl(key string) string { @@ -172,7 +172,7 @@ func (c *cache) UpdateConfig(config string, contentType mime.MimeType) (err erro } func (c *caches) ConvertConfiguration(config string, contentType mime.MimeType, reqType mime.MimeType) (transformed string, err error) { - path := CachesPath + "?action=convert" + path := c.Caches("?action=convert") headers := map[string]string{ "Accept": string(reqType), "Content-Type": string(contentType), @@ -196,7 +196,7 @@ func (c *caches) EqualConfiguration(_, _ string) (bool, error) { } func (c *caches) Names() (names []string, err error) { - rsp, err := c.Get(CachesPath, nil) + rsp, err := c.Get(c.Caches(""), nil) if err = httpClient.ValidateResponse(rsp, err, "getting caches", http.StatusOK); err != nil { return } diff --git a/pkg/infinispan/client/v13/container.go b/pkg/infinispan/client/v13/container.go index d6ecdb717..1b896dd4f 100644 --- a/pkg/infinispan/client/v13/container.go +++ b/pkg/infinispan/client/v13/container.go @@ -10,19 +10,13 @@ import ( "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" ) -const ( - CacheManagerPath = BasePath + "/cache-managers/default" - ContainerPath = BasePath + "/container" - HealthPath = CacheManagerPath + "/health" - HealthStatusPath = HealthPath + "/status" -) - -type container struct { +type Container struct { + api.PathResolver httpClient.HttpClient } -func (c *container) Info() (info *api.ContainerInfo, err error) { - rsp, err := c.Get(CacheManagerPath, nil) +func (c *Container) Info() (info *api.ContainerInfo, err error) { + rsp, err := c.Get(c.CacheManager(""), nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -37,8 +31,8 @@ func (c *container) Info() (info *api.ContainerInfo, err error) { return } -func (c *container) HealthStatus() (status api.HealthStatus, err error) { - rsp, err := c.Get(HealthStatusPath, nil) +func (c *Container) HealthStatus() (status api.HealthStatus, err error) { + rsp, err := c.Get(c.CacheManager("/health/status"), nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -53,8 +47,8 @@ func (c *container) HealthStatus() (status api.HealthStatus, err error) { return api.HealthStatus(string(all)), nil } -func (c *container) Members() (members []string, err error) { - rsp, err := c.Get(HealthPath, nil) +func (c *Container) Members() (members []string, err error) { + rsp, err := c.Get(c.CacheManager("/health"), nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -76,20 +70,20 @@ func (c *container) Members() (members []string, err error) { return health.ClusterHealth.Nodes, nil } -func (c *container) Backups() api.Backups { - return &backups{c.HttpClient} +func (c *Container) Backups() api.Backups { + return &backups{c.PathResolver, c.HttpClient} } -func (c *container) RebalanceDisable() error { +func (c *Container) RebalanceDisable() error { return c.rebalance("disable-rebalancing") } -func (c *container) RebalanceEnable() error { +func (c *Container) RebalanceEnable() error { return c.rebalance("enable-rebalancing") } -func (c *container) rebalance(action string) error { - rsp, err := c.Post(CacheManagerPath+"?action="+action, "", nil) +func (c *Container) rebalance(action string) error { + rsp, err := c.Post(c.CacheManager("?action="+action), "", nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -97,12 +91,12 @@ func (c *container) rebalance(action string) error { return err } -func (c *container) Restores() api.Restores { - return &restores{c.HttpClient} +func (c *Container) Restores() api.Restores { + return &restores{c.PathResolver, c.HttpClient} } -func (c *container) Shutdown() (err error) { - rsp, err := c.Post(ContainerPath+"?action=shutdown", "", nil) +func (c *Container) Shutdown() (err error) { + rsp, err := c.Post(c.Container("?action=shutdown"), "", nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -110,6 +104,6 @@ func (c *container) Shutdown() (err error) { return err } -func (c *container) Xsite() api.Xsite { - return &xsite{c.HttpClient} +func (c *Container) Xsite() api.Xsite { + return &xsite{c.HttpClient, c.PathResolver} } diff --git a/pkg/infinispan/client/v13/infinispan_v13.go b/pkg/infinispan/client/v13/infinispan_v13.go index 336442430..b9c563204 100644 --- a/pkg/infinispan/client/v13/infinispan_v13.go +++ b/pkg/infinispan/client/v13/infinispan_v13.go @@ -7,32 +7,36 @@ import ( ) const ( - BasePath = "rest/v2" MajorVersion = "13" ) type infinispan struct { + api.PathResolver http.HttpClient } func New(client http.HttpClient) api.Infinispan { - return &infinispan{client} + return NewWithPathResolver(client, NewPathResolver()) +} + +func NewWithPathResolver(client http.HttpClient, pathResolver api.PathResolver) api.Infinispan { + return &infinispan{pathResolver, client} } func (i *infinispan) Cache(name string) api.Cache { - return &cache{i.HttpClient, name} + return &cache{i.PathResolver, i.HttpClient, name} } func (i *infinispan) Caches() api.Caches { - return &caches{i.HttpClient} + return &caches{i.PathResolver, i.HttpClient} } func (i *infinispan) Container() api.Container { - return &container{i.HttpClient} + return &Container{i.PathResolver, i.HttpClient} } func (i *infinispan) Logging() api.Logging { - return &logging{i.HttpClient} + return &logging{i.PathResolver, i.HttpClient} } func (i *infinispan) Metrics() api.Metrics { @@ -48,5 +52,5 @@ func (i *infinispan) ScriptCacheName() string { } func (i *infinispan) Server() api.Server { - return &server{i.HttpClient} + return &server{i.PathResolver, i.HttpClient} } diff --git a/pkg/infinispan/client/v13/logging.go b/pkg/infinispan/client/v13/logging.go index 1ee6837f1..549bad14c 100644 --- a/pkg/infinispan/client/v13/logging.go +++ b/pkg/infinispan/client/v13/logging.go @@ -7,16 +7,16 @@ import ( "strings" httpClient "github.com/infinispan/infinispan-operator/pkg/http" + "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" ) -const LoggersPath = BasePath + "/logging/loggers" - type logging struct { + api.PathResolver httpClient.HttpClient } func (l *logging) GetLoggers() (lm map[string]string, err error) { - rsp, err := l.Get(LoggersPath, nil) + rsp, err := l.Get(l.Logging(""), nil) defer func() { err = httpClient.CloseBody(rsp, err) }() @@ -43,7 +43,7 @@ func (l *logging) GetLoggers() (lm map[string]string, err error) { } func (l *logging) SetLogger(name, level string) error { - path := fmt.Sprintf("%s/%s?level=%s", LoggersPath, name, strings.ToUpper(level)) + path := l.Logging(fmt.Sprintf("/%s?level=%s", name, strings.ToUpper(level))) rsp, err := l.Put(path, "", nil) defer func() { err = httpClient.CloseBody(rsp, err) diff --git a/pkg/infinispan/client/v13/path_resolver.go b/pkg/infinispan/client/v13/path_resolver.go new file mode 100644 index 000000000..6ba959bc4 --- /dev/null +++ b/pkg/infinispan/client/v13/path_resolver.go @@ -0,0 +1,31 @@ +package v13 + +import "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" + +func NewPathResolver() api.PathResolver { + return &pathResolver{Root: "rest/v2"} +} + +type pathResolver struct { + Root string +} + +func (r *pathResolver) Caches(s string) string { + return r.Root + "/caches" + s +} + +func (r *pathResolver) CacheManager(s string) string { + return r.Root + "/cache-managers/default" + s +} + +func (r *pathResolver) Container(s string) string { + return r.Root + "/container" + s +} + +func (r *pathResolver) Logging(s string) string { + return r.Root + "/logging/loggers" + s +} + +func (r *pathResolver) Server(s string) string { + return r.Root + "/server" + s +} diff --git a/pkg/infinispan/client/v13/server.go b/pkg/infinispan/client/v13/server.go index 5b93190bd..04c78f887 100644 --- a/pkg/infinispan/client/v13/server.go +++ b/pkg/infinispan/client/v13/server.go @@ -4,16 +4,16 @@ import ( "net/http" httpClient "github.com/infinispan/infinispan-operator/pkg/http" + "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" ) -const ServerPath = BasePath + "/server" - type server struct { + api.PathResolver httpClient.HttpClient } func (s *server) Stop() (err error) { - rsp, err := s.Post(ServerPath+"?action=stop", "", nil) + rsp, err := s.Post(s.Server("?action=stop"), "", nil) defer func() { err = httpClient.CloseBody(rsp, err) }() diff --git a/pkg/infinispan/client/v13/xsite.go b/pkg/infinispan/client/v13/xsite.go index 12e7d3d19..82e4bcad5 100644 --- a/pkg/infinispan/client/v13/xsite.go +++ b/pkg/infinispan/client/v13/xsite.go @@ -6,16 +6,17 @@ import ( "net/http" httpClient "github.com/infinispan/infinispan-operator/pkg/http" + "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" ) -const XSitePath = CacheManagerPath + "/x-site/backups" - type xsite struct { httpClient.HttpClient + api.PathResolver } func (x *xsite) PushAllState() (err error) { - rsp, err := x.Get(XSitePath, nil) + xsitePath := x.CacheManager("/x-site/backups") + rsp, err := x.Get(xsitePath, nil) if err = httpClient.ValidateResponse(rsp, err, "Retrieving xsite status", http.StatusOK); err != nil { return } @@ -38,7 +39,7 @@ func (x *xsite) PushAllState() (err error) { // Statuses will be empty if no xsite caches are configured for k, v := range statuses { if v.Status == "online" { - url := fmt.Sprintf("%s/%s?action=start-push-state", XSitePath, k) + url := fmt.Sprintf("%s/%s?action=start-push-state", xsitePath, k) rsp, err = x.Post(url, "", nil) if err = httpClient.ValidateResponse(rsp, err, "Pushing xsite state", http.StatusOK); err != nil { return diff --git a/pkg/infinispan/client/v14/caches.go b/pkg/infinispan/client/v14/caches.go index e7aff8e19..3e62b0a59 100644 --- a/pkg/infinispan/client/v14/caches.go +++ b/pkg/infinispan/client/v14/caches.go @@ -5,16 +5,16 @@ import ( httpClient "github.com/infinispan/infinispan-operator/pkg/http" "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" - v13 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v13" ) type caches struct { httpClient.HttpClient api.Caches + api.PathResolver } func (c *caches) EqualConfiguration(a, b string) (bool, error) { - path := v13.CachesPath + "?action=compare" + path := c.PathResolver.Caches("?action=compare") parts := map[string]string{ "a": a, "b": b, diff --git a/pkg/infinispan/client/v14/infinispan_v14.go b/pkg/infinispan/client/v14/infinispan_v14.go index ac70a6b40..038292472 100644 --- a/pkg/infinispan/client/v14/infinispan_v14.go +++ b/pkg/infinispan/client/v14/infinispan_v14.go @@ -9,12 +9,18 @@ import ( type infinispan struct { http.HttpClient ispn13 api.Infinispan + api.PathResolver } func New(client http.HttpClient) api.Infinispan { + return NewWithPathResolver(client, v13.NewPathResolver()) +} + +func NewWithPathResolver(client http.HttpClient, pathResolver api.PathResolver) api.Infinispan { return &infinispan{ - HttpClient: client, - ispn13: v13.New(client), + HttpClient: client, + ispn13: v13.NewWithPathResolver(client, pathResolver), + PathResolver: pathResolver, } } @@ -24,8 +30,9 @@ func (i *infinispan) Cache(name string) api.Cache { func (i *infinispan) Caches() api.Caches { return &caches{ - HttpClient: i.HttpClient, - Caches: i.ispn13.Caches(), + HttpClient: i.HttpClient, + Caches: i.ispn13.Caches(), + PathResolver: i.PathResolver, } } diff --git a/pkg/infinispan/client/v15/infinispan_v15.go b/pkg/infinispan/client/v15/infinispan_v15.go new file mode 100644 index 000000000..be992e75c --- /dev/null +++ b/pkg/infinispan/client/v15/infinispan_v15.go @@ -0,0 +1,19 @@ +package v15 + +import ( + "github.com/infinispan/infinispan-operator/pkg/http" + "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" + v14 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v14" +) + +type infinispan struct { + http.HttpClient + api.Infinispan +} + +func New(client http.HttpClient) api.Infinispan { + return &infinispan{ + HttpClient: client, + Infinispan: v14.NewWithPathResolver(client, NewPathResolver()), + } +} diff --git a/pkg/infinispan/client/v15/path_resolver.go b/pkg/infinispan/client/v15/path_resolver.go new file mode 100644 index 000000000..f4d96ec57 --- /dev/null +++ b/pkg/infinispan/client/v15/path_resolver.go @@ -0,0 +1,18 @@ +package v15 + +import ( + "github.com/infinispan/infinispan-operator/pkg/infinispan/client/api" + v13 "github.com/infinispan/infinispan-operator/pkg/infinispan/client/v13" +) + +func NewPathResolver() api.PathResolver { + return &pathResolver{v13.NewPathResolver()} +} + +type pathResolver struct { + api.PathResolver +} + +func (r *pathResolver) CacheManager(s string) string { + return r.Container(s) +} diff --git a/pkg/reconcile/pipeline/infinispan/api.go b/pkg/reconcile/pipeline/infinispan/api.go index 492f2eb99..ccf3a10ab 100644 --- a/pkg/reconcile/pipeline/infinispan/api.go +++ b/pkg/reconcile/pipeline/infinispan/api.go @@ -83,6 +83,9 @@ type Context interface { // InfinispanClientForPod returns a client for the specific pod InfinispanClientForPod(podName string) ispnApi.Infinispan + // InfinispanClientUnknownVersion returns a client for a specified pod based upon the version returned by the server + InfinispanClientUnknownVersion(podName string) (ispnApi.Infinispan, error) + // InfinispanPods returns all pods associated with the Infinispan cluster's StatefulSet // The list is created Lazily and cached per Pipeline execution to prevent repeated calls to retrieve the cluster pods // If an error is returned, then RetryProcessing is automatically set diff --git a/pkg/reconcile/pipeline/infinispan/api_mocks.go b/pkg/reconcile/pipeline/infinispan/api_mocks.go index 4c104e9ab..4617211ec 100644 --- a/pkg/reconcile/pipeline/infinispan/api_mocks.go +++ b/pkg/reconcile/pipeline/infinispan/api_mocks.go @@ -270,6 +270,21 @@ func (mr *MockContextMockRecorder) InfinispanClientForPod(podName interface{}) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InfinispanClientForPod", reflect.TypeOf((*MockContext)(nil).InfinispanClientForPod), podName) } +// InfinispanClientUnknownVersion mocks base method. +func (m *MockContext) InfinispanClientUnknownVersion(podName string) (api.Infinispan, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InfinispanClientUnknownVersion", podName) + ret0, _ := ret[0].(api.Infinispan) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InfinispanClientUnknownVersion indicates an expected call of InfinispanClientUnknownVersion. +func (mr *MockContextMockRecorder) InfinispanClientUnknownVersion(podName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InfinispanClientUnknownVersion", reflect.TypeOf((*MockContext)(nil).InfinispanClientUnknownVersion), podName) +} + // InfinispanPods mocks base method. func (m *MockContext) InfinispanPods() (*v10.PodList, error) { m.ctrl.T.Helper() diff --git a/pkg/reconcile/pipeline/infinispan/context/context.go b/pkg/reconcile/pipeline/infinispan/context/context.go index b10ab058d..a91f97a00 100644 --- a/pkg/reconcile/pipeline/infinispan/context/context.go +++ b/pkg/reconcile/pipeline/infinispan/context/context.go @@ -113,6 +113,11 @@ func (c *contextImpl) InfinispanClientForPod(podName string) api.Infinispan { return ispnClient.New(c.Operand(), curlClient) } +func (c *contextImpl) InfinispanClientUnknownVersion(podName string) (api.Infinispan, error) { + curlClient := c.curlClient(podName) + return ispnClient.NewUnknownVersion(curlClient) +} + func (c *contextImpl) InfinispanPods() (*corev1.PodList, error) { if c.ispnPods == nil { statefulSet := &appsv1.StatefulSet{} diff --git a/pkg/reconcile/pipeline/infinispan/handler/manage/upgrades.go b/pkg/reconcile/pipeline/infinispan/handler/manage/upgrades.go index 29a159fc8..6b429fa2f 100644 --- a/pkg/reconcile/pipeline/infinispan/handler/manage/upgrades.go +++ b/pkg/reconcile/pipeline/infinispan/handler/manage/upgrades.go @@ -122,7 +122,11 @@ func GracefulShutdown(i *ispnv1.Infinispan, ctx pipeline.Context) { } for idx, pod := range podList.Items { - ispnClient := ctx.InfinispanClientForPod(pod.Name) + ispnClient, err := ctx.InfinispanClientUnknownVersion(pod.Name) + if err != nil { + ctx.Requeue(fmt.Errorf("unable to create Infinispan client for cluster being upgraded: %w", err)) + return + } if idx == 0 { if err := ispnClient.Container().RebalanceDisable(); err != nil { ctx.Requeue(fmt.Errorf("unable to disable rebalancing: %w", err)) diff --git a/test/e2e/hotrod-rolling-upgrade/hotrod_rolling_upgrade_test.go b/test/e2e/hotrod-rolling-upgrade/hotrod_rolling_upgrade_test.go index d98874e89..b3fdf5089 100644 --- a/test/e2e/hotrod-rolling-upgrade/hotrod_rolling_upgrade_test.go +++ b/test/e2e/hotrod-rolling-upgrade/hotrod_rolling_upgrade_test.go @@ -85,7 +85,8 @@ func TestRollingUpgrade(t *testing.T) { testKube.Create(spec) testKube.WaitForInfinispanPods(replicas, tutils.SinglePodTimeout, spec.Name, tutils.Namespace) spec = testKube.WaitForInfinispanCondition(spec.Name, tutils.Namespace, ispnv1.ConditionWellFormed) - client := tutils.HTTPClientForCluster(spec, testKube) + versionManager := testKube.VersionManagerFromCSV(sub) + client := tutils.HTTPClientForClusterWithVersionManager(spec, testKube, versionManager) // Create caches createCache("textCache", mime.TextPlain, client) @@ -118,17 +119,7 @@ func TestRollingUpgrade(t *testing.T) { // https://github.com/infinispan/infinispan-operator/issues/1719 time.Sleep(time.Minute) - operands := func() *version.Manager { - testKube.SetRelatedImagesEnvs(sub) - operandVersions := testKube.InstalledCSVEnv(ispnv1.OperatorOperandVersionEnvVarName, sub) - if operandVersions == "" { - panic(fmt.Sprintf("%s env empty, cannot continue", ispnv1.OperatorOperandVersionEnvVarName)) - } - versionManager, err := version.ManagerFromJson(operandVersions) - tutils.ExpectNoError(err) - return versionManager - } - + versionManager = testKube.VersionManagerFromCSV(sub) assertMigration := func(expectedImage string, isRollingUpgrade, indexedSupported bool) { if !isRollingUpgrade { clusterCounter++ @@ -147,7 +138,8 @@ func TestRollingUpgrade(t *testing.T) { } } - if !tutils.CheckExternalAddress(client) { + operand := tutils.Operand(spec.Spec.Version, versionManager) + if !tutils.CheckExternalAddress(client, operand) { panic("Error contacting server") } @@ -171,7 +163,7 @@ func TestRollingUpgrade(t *testing.T) { } // This is the first upgrade to an Operator with multi-operand support, so wait for the oldest Operand - oldestOperand := operands().Oldest() + oldestOperand := versionManager.Oldest() ispnPreUpgrade = testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool { return i.IsConditionTrue(ispnv1.ConditionWellFormed) && i.Status.Operand.Version == oldestOperand.Ref() && @@ -188,9 +180,9 @@ func TestRollingUpgrade(t *testing.T) { } } - versionManager := operands() latestOperand := versionManager.Latest() currentOperand, err := versionManager.WithRef(ispnPreUpgrade.Spec.Version) + client = tutils.HTTPClientForClusterWithVersionManager(spec, testKube, versionManager) tutils.ExpectNoError(err) if !currentOperand.EQ(latestOperand) { ispn := testKube.WaitForInfinispanConditionWithTimeout(spec.Name, tutils.Namespace, ispnv1.ConditionWellFormed, conditionTimeout) @@ -214,7 +206,7 @@ func TestRollingUpgrade(t *testing.T) { i.Status.Operand.Image == latestOperand.Image && i.Status.Operand.Phase == ispnv1.OperandPhaseRunning }) - lastOperand, err := operands().WithRef(ispnPreUpgrade.Status.Operand.Version) + lastOperand, err := versionManager.WithRef(ispnPreUpgrade.Status.Operand.Version) tutils.ExpectNoError(err) isRolling := latestOperand.CVE && lastOperand.UpstreamVersion.EQ(*latestOperand.UpstreamVersion) assertMigration(latestOperand.Image, isRolling, indexSupported) diff --git a/test/e2e/infinispan/authentication_test.go b/test/e2e/infinispan/authentication_test.go index 275a306a8..fd80a510e 100644 --- a/test/e2e/infinispan/authentication_test.go +++ b/test/e2e/infinispan/authentication_test.go @@ -111,7 +111,7 @@ func TestExplicitCredentials(t *testing.T) { } func testAuthentication(ispn *ispnv1.Infinispan, schema, usr, pass string) { - client_ := testKube.WaitForExternalService(ispn, tutils.RouteTimeout, tutils.NewHTTPClient(usr, pass, schema)) + client_ := testKube.WaitForExternalService(ispn, tutils.RouteTimeout, tutils.NewHTTPClient(usr, pass, schema), nil) badCredClient := tutils.NewHTTPClient("badUser", "badPass", schema) badCredClient.SetHostAndPort(client_.GetHostAndPort()) @@ -168,7 +168,7 @@ func TestAuthenticationDisabled(t *testing.T) { // Ensure that rest requests do not require authentication schema := testKube.GetSchemaForRest(spec) - client_ := testKube.WaitForExternalService(spec, tutils.RouteTimeout, tutils.NewHTTPClientNoAuth(schema)) + client_ := testKube.WaitForExternalService(spec, tutils.RouteTimeout, tutils.NewHTTPClientNoAuth(schema), nil) rsp, err := client_.Get("rest/v2/caches", nil) tutils.ExpectNoError(err) if rsp.StatusCode != http.StatusOK { diff --git a/test/e2e/infinispan/authorization_test.go b/test/e2e/infinispan/authorization_test.go index 08be13422..ea7ba367e 100644 --- a/test/e2e/infinispan/authorization_test.go +++ b/test/e2e/infinispan/authorization_test.go @@ -115,7 +115,7 @@ func testAuthorization(ispn *v1.Infinispan, createIdentities func() users.Identi schema := testKube.GetSchemaForRest(ispn) user := identities.Credentials[0].Username pass := identities.Credentials[0].Password - client_ := testKube.WaitForExternalService(ispn, tutils.RouteTimeout, tutils.NewHTTPClient(user, pass, schema)) + client_ := testKube.WaitForExternalService(ispn, tutils.RouteTimeout, tutils.NewHTTPClient(user, pass, schema), nil) // Verify authorization works as expected verify(client_) diff --git a/test/e2e/upgrade/upgrade_test.go b/test/e2e/upgrade/upgrade_test.go index bccb4f39e..24715f400 100644 --- a/test/e2e/upgrade/upgrade_test.go +++ b/test/e2e/upgrade/upgrade_test.go @@ -84,7 +84,7 @@ func TestUpgrade(t *testing.T) { // Add a persistent cache with data to ensure contents can be read after upgrade(s) numEntries := 100 - client := tutils.HTTPClientForCluster(spec, testKube) + client := tutils.HTTPClientForClusterWithVersionManager(spec, testKube, testKube.VersionManagerFromCSV(sub)) peristentCache := "persistentCache" cache := tutils.NewCacheHelper(peristentCache, client) @@ -156,17 +156,7 @@ func TestUpgrade(t *testing.T) { } } - operands := func() *version.Manager { - testKube.SetRelatedImagesEnvs(sub) - operandVersions := testKube.InstalledCSVEnv(ispnv1.OperatorOperandVersionEnvVarName, sub) - if operandVersions == "" { - panic(fmt.Sprintf("%s env empty, cannot continue", ispnv1.OperatorOperandVersionEnvVarName)) - } - versionManager, err := version.ManagerFromJson(operandVersions) - tutils.ExpectNoError(err) - return versionManager - } - + versionManager := testKube.VersionManagerFromCSV(sub) if ispnPreUpgrade.Spec.Version == "" { relatedImageJdk := testKube.InstalledCSVEnv("RELATED_IMAGE_OPENJDK", sub) if relatedImageJdk != "" { @@ -185,7 +175,7 @@ func TestUpgrade(t *testing.T) { } // This is the first upgrade to an Operator with multi-operand support, so wait for the oldest Operand - oldestOperand := operands().Oldest() + oldestOperand := versionManager.Oldest() ispnPreUpgrade = testKube.WaitForInfinispanState(spec.Name, spec.Namespace, func(i *ispnv1.Infinispan) bool { return i.IsConditionTrue(ispnv1.ConditionWellFormed) && i.Status.Operand.Version == oldestOperand.Ref() && @@ -196,7 +186,7 @@ func TestUpgrade(t *testing.T) { } // Upgrade to the latest available Operand - latestOperand := operands().Latest() + latestOperand := versionManager.Latest() if ispnPreUpgrade.Spec.Version != latestOperand.Ref() { ispn := testKube.WaitForInfinispanConditionWithTimeout(spec.Name, tutils.Namespace, ispnv1.ConditionWellFormed, conditionTimeout) tutils.ExpectNoError( @@ -222,7 +212,7 @@ func TestUpgrade(t *testing.T) { // Ensure that persistent cache entries have survived the upgrade(s) // Refresh the hostAddr and client as the url will change if NodePort is used. - client = tutils.HTTPClientForCluster(spec, testKube) + client = tutils.HTTPClientForClusterWithVersionManager(spec, testKube, versionManager) tutils.NewCacheHelper(peristentCache, client).AssertSize(numEntries) // Restore the backup and ensure that the cache exists with the expected number of entries @@ -270,7 +260,8 @@ func TestUpgrade(t *testing.T) { testKube.WaitForInfinispanConditionWithTimeout(spec.Name, tutils.Namespace, ispnv1.ConditionWellFormed, conditionTimeout) // Ensure that persistent cache entries still contain the expected numEntries - client = tutils.HTTPClientForCluster(spec, testKube) + versionManager := testKube.VersionManagerFromCSV(sub) + client = tutils.HTTPClientForClusterWithVersionManager(spec, testKube, versionManager) tutils.NewCacheHelper(peristentCache, client).AssertSize(numEntries) } diff --git a/test/e2e/utils/cache.go b/test/e2e/utils/cache.go index 984c5ecfd..53af29034 100644 --- a/test/e2e/utils/cache.go +++ b/test/e2e/utils/cache.go @@ -152,7 +152,7 @@ func (c *CacheHelper) Available(available bool) { } else { availability = "DEGRADED_MODE" } - path := fmt.Sprintf("%s/%s?action=set-availability&availability=%s", v13.CachesPath, url.PathEscape(c.CacheName), availability) + path := v13.NewPathResolver().Caches(fmt.Sprintf("/%s?action=set-availability&availability=%s", url.PathEscape(c.CacheName), availability)) _, err := c.Client.Post(path, "", nil) ExpectNoError(err) } diff --git a/test/e2e/utils/common.go b/test/e2e/utils/common.go index a667cf6b3..6bdfe838d 100644 --- a/test/e2e/utils/common.go +++ b/test/e2e/utils/common.go @@ -395,7 +395,11 @@ func clientForCluster(i *ispnv1.Infinispan, kube *TestKubernetes) HTTPClient { } func HTTPClientForCluster(i *ispnv1.Infinispan, kube *TestKubernetes) HTTPClient { - return kube.WaitForExternalService(i, RouteTimeout, clientForCluster(i, kube)) + return HTTPClientForClusterWithVersionManager(i, kube, nil) +} + +func HTTPClientForClusterWithVersionManager(i *ispnv1.Infinispan, kube *TestKubernetes, manager *version.Manager) HTTPClient { + return kube.WaitForExternalService(i, RouteTimeout, clientForCluster(i, kube), manager) } func HTTPSClientForCluster(i *ispnv1.Infinispan, tlsConfig *tls.Config, kube *TestKubernetes) HTTPClient { @@ -424,5 +428,15 @@ func HTTPSClientForCluster(i *ispnv1.Infinispan, tlsConfig *tls.Config, kube *Te client = NewClient(authNone, nil, nil, "https", tlsConfig) } } - return kube.WaitForExternalService(i, RouteTimeout, client) + return kube.WaitForExternalService(i, RouteTimeout, client, nil) +} + +// Operand replicates the semantics of InitialiseOperandVersion pipeline handler for determing Operand version when no version is explicitly provided +func Operand(ref string, manager *version.Manager) version.Operand { + if ref == "" { + return manager.Oldest() + } + operand, err := manager.WithRef(ref) + ExpectNoError(err) + return operand } diff --git a/test/e2e/utils/kubernetes.go b/test/e2e/utils/kubernetes.go index 3bbec9916..3596a4dbd 100644 --- a/test/e2e/utils/kubernetes.go +++ b/test/e2e/utils/kubernetes.go @@ -18,6 +18,7 @@ import ( consts "github.com/infinispan/infinispan-operator/controllers/constants" "github.com/infinispan/infinispan-operator/launcher/operator" ispnClient "github.com/infinispan/infinispan-operator/pkg/infinispan/client" + "github.com/infinispan/infinispan-operator/pkg/infinispan/version" kube "github.com/infinispan/infinispan-operator/pkg/kubernetes" routev1 "github.com/openshift/api/route/v1" "go.uber.org/zap/zapcore" @@ -493,7 +494,7 @@ func (k TestKubernetes) WaitForCrd(crd *apiextv1.CustomResourceDefinition) { // WaitForExternalService checks if an http server is listening at the endpoint exposed by the service (ns, name) // The HostAndPort of the provided HTTPClient is updated to use the external service when available -func (k TestKubernetes) WaitForExternalService(ispn *ispnv1.Infinispan, timeout time.Duration, client HTTPClient) HTTPClient { +func (k TestKubernetes) WaitForExternalService(ispn *ispnv1.Infinispan, timeout time.Duration, client HTTPClient, versionManager *version.Manager) HTTPClient { err := wait.Poll(DefaultPollPeriod, timeout, func() (done bool, err error) { var hostAndPort string switch ispn.GetExposeType() { @@ -524,7 +525,12 @@ func (k TestKubernetes) WaitForExternalService(ispn *ispnv1.Infinispan, timeout return false, nil } client.SetHostAndPort(hostAndPort) - return CheckExternalAddress(client), nil + + if versionManager == nil { + versionManager = VersionManager() + } + operand := Operand(ispn.Spec.Version, versionManager) + return CheckExternalAddress(client, operand), nil }) ExpectNoError(err) return client @@ -570,8 +576,8 @@ func getNodePort(service *corev1.Service) int32 { return service.Spec.Ports[0].NodePort } -func CheckExternalAddress(client HTTPClient) bool { - status, err := ispnClient.New(LatestOperand, client).Container().HealthStatus() +func CheckExternalAddress(client HTTPClient, operand version.Operand) bool { + status, err := ispnClient.New(operand, client).Container().HealthStatus() if isTemporary(err) { return false } diff --git a/test/e2e/utils/olm.go b/test/e2e/utils/olm.go index 1bc279f21..c807aa0f9 100644 --- a/test/e2e/utils/olm.go +++ b/test/e2e/utils/olm.go @@ -9,7 +9,9 @@ import ( "strings" "testing" + ispnv1 "github.com/infinispan/infinispan-operator/api/v1" "github.com/infinispan/infinispan-operator/controllers/constants" + "github.com/infinispan/infinispan-operator/pkg/infinispan/version" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -110,6 +112,10 @@ func (k TestKubernetes) CreateSubscriptionAndApproveInitialVersion(olm OLMEnv, s k.WaitForPods(1, ConditionWaitTimeout, &client.ListOptions{ LabelSelector: labels.SelectorFromSet(map[string]string{"app.kubernetes.io/name": "infinispan-operator"}), }, nil) + + k.WaitForSubscription(sub, func() bool { + return sub.Status.InstalledCSV != "" + }) } func (k TestKubernetes) CleanupOLMTest(t *testing.T, testIdentifier, subName, subNamespace, subPackage string) { @@ -411,3 +417,14 @@ func retryOnConflict(update func() error) { }) ExpectNoError(err) } + +func (k TestKubernetes) VersionManagerFromCSV(sub *coreos.Subscription) *version.Manager { + k.SetRelatedImagesEnvs(sub) + operandVersions := k.InstalledCSVEnv(ispnv1.OperatorOperandVersionEnvVarName, sub) + if operandVersions == "" { + panic(fmt.Sprintf("%s env empty, cannot continue", ispnv1.OperatorOperandVersionEnvVarName)) + } + versionManager, err := version.ManagerFromJson(operandVersions) + ExpectNoError(err) + return versionManager +} diff --git a/test/e2e/xsite/xsite_test.go b/test/e2e/xsite/xsite_test.go index 92a689d8f..044893679 100644 --- a/test/e2e/xsite/xsite_test.go +++ b/test/e2e/xsite/xsite_test.go @@ -12,6 +12,8 @@ import ( ispnv1 "github.com/infinispan/infinispan-operator/api/v1" ispnv2 "github.com/infinispan/infinispan-operator/api/v2alpha1" "github.com/infinispan/infinispan-operator/controllers/constants" + "github.com/infinispan/infinispan-operator/pkg/infinispan/version" + "github.com/infinispan/infinispan-operator/pkg/kubernetes" kube "github.com/infinispan/infinispan-operator/pkg/kubernetes" "github.com/infinispan/infinispan-operator/pkg/mime" tutils "github.com/infinispan/infinispan-operator/test/e2e/utils" @@ -828,7 +830,8 @@ func testBackupCrossSiteCache(t *testing.T, useTLS bool) { assert.Contains(t, ispnXSite2.GetCondition(ispnv1.ConditionCrossSiteViewFormed).Message, "xsite1,xsite2") //Populate the cluster with some data to backup - client := tutils.HTTPClientForCluster(&testKubes["xsite1"].crossSite, testKubes["xsite1"].kube) + versionManager := getVersionManager(t, testKubes["xsite1"].kube) + client := tutils.HTTPClientForClusterWithVersionManager(&testKubes["xsite1"].crossSite, testKubes["xsite1"].kube, versionManager) cacheName := "xsiteCache" cache := tutils.NewCacheHelper(cacheName, client) @@ -867,3 +870,19 @@ func backupSpec(testName, name, namespace, cluster string) *ispnv2.Backup { spec.Default() return spec } + +func getVersionManager(t *testing.T, kube *tutils.TestKubernetes) *version.Manager { + podList := kube.WaitForPods(1, tutils.SinglePodTimeout, &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + "control-plane": "controller-manager", + })}, + nil, + ) + assert.Equal(t, 1, len(podList.Items)) + envVars := podList.Items[0].Spec.Containers[0].Env + index := kubernetes.GetEnvVarIndex(ispnv1.OperatorOperandVersionEnvVarName, &envVars) + assert.GreaterOrEqual(t, index, 0) + versionManager, err := version.ManagerFromJson(envVars[index].Value) + tutils.ExpectNoError(err) + return versionManager +}