Validate Version (#32)

and product. Version should be >= 4.0 and product should be RabbitMQ.
closes: https://github.com/rabbitmq/rabbitmq-amqp-go-client/issues/26

Signed-off-by: Gabriele Santomaggio <G.santomaggio@gmail.com>
This commit is contained in:
Gabriele Santomaggio 2025-02-18 15:06:04 +01:00 committed by GitHub
parent 4f689b7546
commit db3f233aef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 240 additions and 4 deletions

View File

@ -56,6 +56,9 @@ type AmqpConnOptions struct {
}
type AmqpConnection struct {
properties map[string]any
featuresAvailable *featuresAvailable
azureConnection *amqp.Conn
id string
management *AmqpManagement
@ -66,6 +69,11 @@ type AmqpConnection struct {
entitiesTracker *entitiesTracker
}
func (a *AmqpConnection) Properties() map[string]any {
return a.properties
}
// NewPublisher creates a new Publisher that sends messages to the provided destination.
// The destination is a TargetAddress that can be a Queue or an Exchange with a routing key.
// See QueueAddress and ExchangeAddress for more information.
@ -133,6 +141,7 @@ func Dial(ctx context.Context, addresses []string, connOptions *AmqpConnOptions,
lifeCycle: NewLifeCycle(),
amqpConnOptions: connOptions,
entitiesTracker: newEntitiesTracker(),
featuresAvailable: newFeaturesAvailable(),
}
tmp := make([]string, len(addresses))
copy(tmp, addresses)
@ -188,6 +197,20 @@ func (a *AmqpConnection) open(ctx context.Context, addresses []string, connOptio
Error("Failed to open connection", ExtractWithoutPassword(addr), err)
continue
}
a.properties = azureConnection.Properties()
err = a.featuresAvailable.ParseProperties(a.properties)
if err != nil {
Warn("Validate properties Error.", ExtractWithoutPassword(addr), err)
}
if !a.featuresAvailable.is4OrMore {
Warn("The server version is less than 4.0.0", ExtractWithoutPassword(addr))
}
if !a.featuresAvailable.isRabbitMQ {
Warn("The server is not RabbitMQ", ExtractWithoutPassword(addr))
}
Debug("Connected to", ExtractWithoutPassword(addr))
break
}

View File

@ -24,6 +24,8 @@ var _ = Describe("AMQP connection Test", func() {
SASLType: amqp.SASLTypePlain("guest", "guest")})
Expect(err).To(BeNil())
Expect(connection.Properties()["product"]).To(Equal("RabbitMQ"))
err = connection.Close(context.Background())
Expect(err).To(BeNil())
})

View File

@ -0,0 +1,98 @@
package rabbitmqamqp
import (
"fmt"
"regexp"
"strconv"
"strings"
)
type Version struct {
Major int
Minor int
Patch int
}
func (v Version) Compare(other Version) int {
if v.Major != other.Major {
return v.Major - other.Major
}
if v.Minor != other.Minor {
return v.Minor - other.Minor
}
return v.Patch - other.Patch
}
type featuresAvailable struct {
is4OrMore bool
is41OrMore bool
isRabbitMQ bool
}
func newFeaturesAvailable() *featuresAvailable {
return &featuresAvailable{}
}
func (f *featuresAvailable) ParseProperties(properties map[string]any) error {
if properties["version"] == nil {
return fmt.Errorf("missing version property")
}
version := extractVersion(properties["version"].(string))
if version == "" {
return fmt.Errorf("invalid version format: %s", version)
}
f.is4OrMore = isVersionGreaterOrEqual(version, "4.0.0")
f.is41OrMore = isVersionGreaterOrEqual(version, "4.1.0")
f.isRabbitMQ = strings.EqualFold(properties["product"].(string), "RabbitMQ")
return nil
}
func extractVersion(fullVersion string) string {
pattern := `(\d+\.\d+\.\d+)`
regex := regexp.MustCompile(pattern)
match := regex.FindStringSubmatch(fullVersion)
if len(match) > 1 {
return match[1]
}
return ""
}
func parseVersion(version string) (Version, error) {
parts := strings.Split(version, ".")
if len(parts) != 3 {
return Version{}, fmt.Errorf("invalid version format: %s", version)
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return Version{}, fmt.Errorf("invalid major version: %s", parts[0])
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return Version{}, fmt.Errorf("invalid minor version: %s", parts[1])
}
patch, err := strconv.Atoi(parts[2])
if err != nil {
return Version{}, fmt.Errorf("invalid patch version: %s", parts[2])
}
return Version{Major: major, Minor: minor, Patch: patch}, nil
}
func isVersionGreaterOrEqual(version, target string) bool {
v1, err := parseVersion(version)
if err != nil {
return false
}
v2, err := parseVersion(target)
if err != nil {
return false
}
return v1.Compare(v2) >= 0
}

View File

@ -0,0 +1,113 @@
package rabbitmqamqp
import (
"fmt"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Available Features", func() {
It("Parse Version", func() {
v, err := parseVersion("1.2.3")
Expect(err).NotTo(HaveOccurred())
Expect(v).To(Equal(Version{Major: 1, Minor: 2, Patch: 3}))
_, err = parseVersion("1.2")
Expect(err).To(HaveOccurred())
Expect(fmt.Sprintf("%s", err)).To(ContainSubstring("invalid version format: 1.2"))
_, err = parseVersion("error.3.3")
Expect(err).To(HaveOccurred())
Expect(fmt.Sprintf("%s", err)).To(ContainSubstring("invalid major version: error"))
_, err = parseVersion("1.error.3")
Expect(err).To(HaveOccurred())
Expect(fmt.Sprintf("%s", err)).To(ContainSubstring("invalid minor version: error"))
_, err = parseVersion("1.2.error")
Expect(err).To(HaveOccurred())
Expect(fmt.Sprintf("%s", err)).To(ContainSubstring("invalid patch version: error"))
v, err = parseVersion(extractVersion("3.12.1-rc1"))
Expect(err).NotTo(HaveOccurred())
Expect(v).To(Equal(Version{Major: 3, Minor: 12, Patch: 1}))
v, err = parseVersion(extractVersion("3.13.1-alpha.234"))
Expect(err).NotTo(HaveOccurred())
Expect(v).To(Equal(Version{Major: 3, Minor: 13, Patch: 1}))
})
It("Is Version Greater Or Equal", func() {
Expect(isVersionGreaterOrEqual("1.2.3", "1.2.3")).To(BeTrue())
Expect(isVersionGreaterOrEqual("1.2.3", "1.2.2")).To(BeTrue())
Expect(isVersionGreaterOrEqual("1.2.3", "1.2.4")).To(BeFalse())
Expect(isVersionGreaterOrEqual("1.2.3", "1.3.3")).To(BeFalse())
Expect(isVersionGreaterOrEqual("1.2.3", "2.2.3")).To(BeFalse())
Expect(isVersionGreaterOrEqual("3.1.3-alpha.1", "2.2.3")).To(BeFalse())
Expect(isVersionGreaterOrEqual("3.3.3-rc.1", "2.2.3")).To(BeFalse())
Expect(isVersionGreaterOrEqual("error.3.2", "2.2.3")).To(BeFalse())
Expect(isVersionGreaterOrEqual("4.3.2", "2.error.3")).To(BeFalse())
})
It("Available Features check Version", func() {
var availableFeatures = newFeaturesAvailable()
Expect(availableFeatures).NotTo(BeNil())
Expect(availableFeatures.ParseProperties(map[string]any{})).NotTo(BeNil())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "3.9.0",
"product": "RabbitMQ",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeFalse())
Expect(availableFeatures.is41OrMore).To(BeFalse())
Expect(availableFeatures.isRabbitMQ).To(BeTrue())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "3.11.0",
"product": "RabbitMQ",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeFalse())
Expect(availableFeatures.is41OrMore).To(BeFalse())
Expect(availableFeatures.isRabbitMQ).To(BeTrue())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "4.0.6-rc.1",
"product": "RabbitMQ",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeTrue())
Expect(availableFeatures.is41OrMore).To(BeFalse())
Expect(availableFeatures.isRabbitMQ).To(BeTrue())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "4.1.0",
"product": "RabbitMQ",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeTrue())
Expect(availableFeatures.is41OrMore).To(BeTrue())
Expect(availableFeatures.isRabbitMQ).To(BeTrue())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "4.1.0-beta.1",
"product": "Boh",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeTrue())
Expect(availableFeatures.is41OrMore).To(BeTrue())
Expect(availableFeatures.isRabbitMQ).To(BeFalse())
Expect(availableFeatures.ParseProperties(map[string]any{
"version": "4.1.0-rc.8",
"product": "rabbitmq",
})).To(BeNil())
Expect(availableFeatures.is4OrMore).To(BeTrue())
Expect(availableFeatures.is41OrMore).To(BeTrue())
Expect(availableFeatures.isRabbitMQ).To(BeTrue())
})
})