From 6b92a3ff6c45b01099e9c17e73f25eac589092e8 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Mon, 7 Apr 2025 14:16:07 +0200 Subject: [PATCH 1/7] move db version handlers to version.go --- node-registrar/pkg/db/nodes.go | 30 -------------------------- node-registrar/pkg/db/version.go | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 30 deletions(-) create mode 100644 node-registrar/pkg/db/version.go diff --git a/node-registrar/pkg/db/nodes.go b/node-registrar/pkg/db/nodes.go index aa52f5d..973231e 100644 --- a/node-registrar/pkg/db/nodes.go +++ b/node-registrar/pkg/db/nodes.go @@ -80,33 +80,3 @@ func (db *Database) GetUptimeReports(nodeID uint64, start, end time.Time) ([]Upt func (db *Database) CreateUptimeReport(report *UptimeReport) error { return db.gormDB.Create(report).Error } - -func (db *Database) SetZOSVersion(version string) error { - var current ZosVersion - result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version}).FirstOrCreate(¤t) - - if result.Error != nil { - return result.Error - } - - if result.RowsAffected == 0 { - if current.Version == version { - return errors.New("version already set") - } - return db.gormDB.Model(¤t). - Select("version"). - Update("version", version).Error - } - return nil -} - -func (db *Database) GetZOSVersion() (string, error) { - var setting ZosVersion - if err := db.gormDB.Where("key = ?", "zos_4").First(&setting).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return "", ErrRecordNotFound - } - return "", err - } - return setting.Version, nil -} diff --git a/node-registrar/pkg/db/version.go b/node-registrar/pkg/db/version.go new file mode 100644 index 0000000..7741e3f --- /dev/null +++ b/node-registrar/pkg/db/version.go @@ -0,0 +1,37 @@ +package db + +import ( + "errors" + + "gorm.io/gorm" +) + +func (db *Database) SetZOSVersion(version string) error { + var current ZosVersion + result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version}).FirstOrCreate(¤t) + + if result.Error != nil { + return result.Error + } + + if result.RowsAffected == 0 { + if current.Version == version { + return ErrVersionAlreadySet + } + return db.gormDB.Model(¤t). + Select("version"). + Update("version", version).Error + } + return nil +} + +func (db *Database) GetZOSVersion() (string, error) { + var setting ZosVersion + if err := db.gormDB.Where("key = ?", "zos_4").First(&setting).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return "", ErrRecordNotFound + } + return "", err + } + return setting.Version, nil +} From 6d689b34710759dd8e61fb139e3d8cf8bd4fdacf Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Mon, 7 Apr 2025 14:16:58 +0200 Subject: [PATCH 2/7] update registrar version to be not encoded --- go.work.sum | 1 + node-registrar/go.mod | 14 +++++++------- node-registrar/go.sum | 28 +++++++++++++-------------- node-registrar/pkg/db/db.go | 1 + node-registrar/pkg/db/models.go | 5 +++-- node-registrar/pkg/server/handlers.go | 26 ++++++++++++++++++++++++- 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/go.work.sum b/go.work.sum index f8fbfef..9e41d9b 100644 --- a/go.work.sum +++ b/go.work.sum @@ -13,6 +13,7 @@ github.com/vedhavyas/go-subkey v1.0.3/go.mod h1:CloUaFQSSTdWnINfBRFjVMkWXZANW+nd golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/node-registrar/go.mod b/node-registrar/go.mod index 8989bac..78c3e97 100644 --- a/node-registrar/go.mod +++ b/node-registrar/go.mod @@ -6,6 +6,7 @@ require ( github.com/cosmos/go-bip39 v1.0.0 github.com/gin-contrib/cors v1.7.3 github.com/gin-gonic/gin v1.10.0 + github.com/go-playground/validator/v10 v10.26.0 github.com/lib/pq v1.10.9 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.33.0 @@ -28,7 +29,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/base58 v1.0.4 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -36,7 +37,6 @@ require ( github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/goccy/go-json v0.10.4 // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -62,11 +62,11 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect golang.org/x/arch v0.12.0 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/net v0.33.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/tools v0.22.0 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/node-registrar/go.sum b/node-registrar/go.sum index 3f35c5c..c20ea5d 100644 --- a/node-registrar/go.sum +++ b/node-registrar/go.sum @@ -29,8 +29,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/ethereum/go-ethereum v1.10.20 h1:75IW830ClSS40yrQC1ZCMZCt5I+zU16oqId2SiQwdQ4= github.com/ethereum/go-ethereum v1.10.20/go.mod h1:LWUN82TCHGpxB3En5HVmLLzPD7YSrEUFmFfN1nKkVN0= -github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= -github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gin-contrib/cors v1.7.3 h1:hV+a5xp8hwJoTw7OY+a70FsL8JkVVFTXw9EcfrYUdns= github.com/gin-contrib/cors v1.7.3/go.mod h1:M3bcKZhxzsvI+rlRSkkxHyljJt1ESd93COUvemZ79j4= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= @@ -58,8 +58,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -165,8 +165,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -175,12 +175,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -191,8 +191,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -200,8 +200,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/node-registrar/pkg/db/db.go b/node-registrar/pkg/db/db.go index fe0dd15..46a2b2f 100644 --- a/node-registrar/pkg/db/db.go +++ b/node-registrar/pkg/db/db.go @@ -33,6 +33,7 @@ type Database struct { var ( ErrRecordNotFound = errors.New("could not find any records") ErrRecordAlreadyExists = errors.New("record already exists") + ErrVersionAlreadySet = errors.New("version already set") ) func NewDB(c Config) (Database, error) { diff --git a/node-registrar/pkg/db/models.go b/node-registrar/pkg/db/models.go index 7427dfe..b41a695 100644 --- a/node-registrar/pkg/db/models.go +++ b/node-registrar/pkg/db/models.go @@ -64,8 +64,9 @@ type UptimeReport struct { } type ZosVersion struct { - Key string `gorm:"primaryKey;size:50"` - Version string `gorm:"not null"` + Key string `gorm:"primaryKey;size:50"` + Version string `gorm:"not null"` + SafeToUpgrade bool `json:"safe_to_upgrade"` } type Interface struct { diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 0e2534e..7c6ab3f 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -5,11 +5,14 @@ import ( "errors" "fmt" "net/http" + "regexp" "strconv" "strings" "time" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" "github.com/lib/pq" "github.com/threefoldtech/tfgrid4-sdk-go/node-registrar/pkg/db" ) @@ -690,9 +693,12 @@ func (s *Server) getAccountHandler(c *gin.Context) { } type ZOSVersionRequest struct { - Version string `json:"version" binding:"required,base64"` + Version string `json:"version" binding:"required,versionfmt"` + SafeToUpgrade bool `json:"safe_to_upgrade" binding:"required"` } +var versionRegex = regexp.MustCompile(`^v\d+\.\d+\.\d+$`) + // @Summary Set ZOS Version // @Description Sets the ZOS version // @Tags ZOS @@ -712,6 +718,11 @@ func (s *Server) setZOSVersionHandler(c *gin.Context) { return } + if err := addVersionValidator(); err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + var req ZOSVersionRequest if err := c.ShouldBindJSON(&req); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) @@ -783,3 +794,16 @@ func ensureOwner(c *gin.Context, twinID uint64) { return } } + +func addVersionValidator() error { + // Register the custom validation + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + if err := v.RegisterValidation("versionfmt", func(fl validator.FieldLevel) bool { + version := fl.Field().String() + return versionRegex.MatchString(version) + }); err != nil { + return err + } + } + return nil +} From 546d03a97f8999a7e5e81d9abf13da193fc2a4e4 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Mon, 7 Apr 2025 15:15:49 +0200 Subject: [PATCH 3/7] update registrar client to expect ZosVersion instead of encoded string --- node-registrar/client/zos_version.go | 35 ++++------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/node-registrar/client/zos_version.go b/node-registrar/client/zos_version.go index 9ce848d..a7d268a 100644 --- a/node-registrar/client/zos_version.go +++ b/node-registrar/client/zos_version.go @@ -2,11 +2,9 @@ package client import ( "bytes" - "encoding/base64" "encoding/json" "net/http" "net/url" - "strings" "time" "github.com/pkg/errors" @@ -40,20 +38,7 @@ func (c *RegistrarClient) getZosVersion() (version ZosVersion, err error) { defer resp.Body.Close() - var versionString string - err = json.NewDecoder(resp.Body).Decode(&versionString) - if err != nil { - return version, err - } - - versionBytes, err := base64.StdEncoding.DecodeString(versionString) - if err != nil { - return version, err - } - - correctedJSON := strings.ReplaceAll(string(versionBytes), "'", "\"") - - err = json.NewDecoder(strings.NewReader(correctedJSON)).Decode(&version) + err = json.NewDecoder(resp.Body).Decode(&version) if err != nil { return version, err } @@ -77,23 +62,13 @@ func (c *RegistrarClient) setZosVersion(v string, safeToUpgrade bool) (err error SafeToUpgrade: safeToUpgrade, } - jsonData, err := json.Marshal(version) - if err != nil { - return errors.Wrap(err, "failed to marshal zos version") - } - - encodedVersion := struct { - Version string `json:"version"` - }{ - Version: base64.StdEncoding.EncodeToString(jsonData), - } - - jsonData, err = json.Marshal(encodedVersion) + var body bytes.Buffer + err = json.NewEncoder(&body).Encode(version) if err != nil { - return errors.Wrap(err, "failed to marshal zos version in hex format") + return errors.Wrap(err, "failed to encode request body") } - req, err := http.NewRequest("PUT", url, bytes.NewReader(jsonData)) + req, err := http.NewRequest("PUT", url, &body) if err != nil { return errors.Wrap(err, "failed to construct http request to the registrar") } From c42aa288d958af1d2bc0ac4fb24cee705a85ee15 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Mon, 7 Apr 2025 15:45:31 +0200 Subject: [PATCH 4/7] return Zos4Version from db --- node-registrar/pkg/db/version.go | 20 ++++++++++---------- node-registrar/pkg/server/handlers.go | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/node-registrar/pkg/db/version.go b/node-registrar/pkg/db/version.go index 7741e3f..8367360 100644 --- a/node-registrar/pkg/db/version.go +++ b/node-registrar/pkg/db/version.go @@ -2,36 +2,36 @@ package db import ( "errors" + "reflect" "gorm.io/gorm" ) -func (db *Database) SetZOSVersion(version string) error { +func (db *Database) SetZOSVersion(version ZosVersion) error { var current ZosVersion - result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version}).FirstOrCreate(¤t) + result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version.Version}).FirstOrCreate(¤t) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { - if current.Version == version { + if reflect.DeepEqual(current, version) { return ErrVersionAlreadySet } return db.gormDB.Model(¤t). Select("version"). - Update("version", version).Error + Updates(version).Error } return nil } -func (db *Database) GetZOSVersion() (string, error) { - var setting ZosVersion - if err := db.gormDB.Where("key = ?", "zos_4").First(&setting).Error; err != nil { +func (db *Database) GetZOSVersion() (version ZosVersion, err error) { + if err := db.gormDB.Where("key = ?", "zos_4").First(&version).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - return "", ErrRecordNotFound + return version, ErrRecordNotFound } - return "", err + return version, err } - return setting.Version, nil + return version, nil } diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 7c6ab3f..360bf09 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -729,7 +729,7 @@ func (s *Server) setZOSVersionHandler(c *gin.Context) { return } - if err := s.db.SetZOSVersion(req.Version); err != nil { + if err := s.db.SetZOSVersion(db.ZosVersion{Version: req.Version, SafeToUpgrade: req.SafeToUpgrade}); err != nil { status := http.StatusInternalServerError if err.Error() == "version already set" { status = http.StatusConflict From 91cd572a64db379553f1273af2beec276b90eae1 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 8 Apr 2025 14:06:05 +0200 Subject: [PATCH 5/7] fix version update --- node-registrar/pkg/db/version.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/node-registrar/pkg/db/version.go b/node-registrar/pkg/db/version.go index 8367360..2172373 100644 --- a/node-registrar/pkg/db/version.go +++ b/node-registrar/pkg/db/version.go @@ -2,11 +2,12 @@ package db import ( "errors" - "reflect" "gorm.io/gorm" ) +const zos4Key = "zos_4" + func (db *Database) SetZOSVersion(version ZosVersion) error { var current ZosVersion result := db.gormDB.Where(ZosVersion{Key: ZOS4VersionKey}).Attrs(ZosVersion{Version: version.Version}).FirstOrCreate(¤t) @@ -16,18 +17,25 @@ func (db *Database) SetZOSVersion(version ZosVersion) error { } if result.RowsAffected == 0 { - if reflect.DeepEqual(current, version) { + update := map[string]any{} + if current.Version != version.Version { + update["version"] = version.Version + } + if current.SafeToUpgrade != version.SafeToUpgrade { + update["safe_to_upgrade"] = version.SafeToUpgrade + } + if len(update) == 0 { return ErrVersionAlreadySet } return db.gormDB.Model(¤t). - Select("version"). + Where("key = ?", zos4Key). Updates(version).Error } return nil } func (db *Database) GetZOSVersion() (version ZosVersion, err error) { - if err := db.gormDB.Where("key = ?", "zos_4").First(&version).Error; err != nil { + if err := db.gormDB.Where("key = ?", zos4Key).First(&version).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return version, ErrRecordNotFound } From e61bd608dd7c9dc169258236ea88988ca7570ea8 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Wed, 9 Apr 2025 16:25:24 +0200 Subject: [PATCH 6/7] add fallback mechanism go version decoding --- node-registrar/client/zos_version.go | 27 +++++++++++++++++++++++++-- node-registrar/pkg/server/handlers.go | 6 +++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/node-registrar/client/zos_version.go b/node-registrar/client/zos_version.go index a7d268a..cb4a483 100644 --- a/node-registrar/client/zos_version.go +++ b/node-registrar/client/zos_version.go @@ -2,9 +2,12 @@ package client import ( "bytes" + "encoding/base64" "encoding/json" + "io" "net/http" "net/url" + "strings" "time" "github.com/pkg/errors" @@ -35,11 +38,31 @@ func (c *RegistrarClient) getZosVersion() (version ZosVersion, err error) { err = parseResponseError(resp.Body) return version, errors.Wrapf(err, "failed to get zos version with status code %s", resp.Status) } - defer resp.Body.Close() - err = json.NewDecoder(resp.Body).Decode(&version) + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return version, err + } + + err = json.Unmarshal(bodyBytes, &version) if err != nil { + // try decoding base64 version + var versionString string + err = json.Unmarshal(bodyBytes, &versionString) + if err != nil { + return version, err + } + + decodedVersion, err := base64.StdEncoding.DecodeString(versionString) + if err != nil { + return version, err + } + + correctedJSON := strings.ReplaceAll(string(decodedVersion), "'", "\"") + + err = json.Unmarshal([]byte(correctedJSON), &version) + return version, err } diff --git a/node-registrar/pkg/server/handlers.go b/node-registrar/pkg/server/handlers.go index 360bf09..257bf0b 100644 --- a/node-registrar/pkg/server/handlers.go +++ b/node-registrar/pkg/server/handlers.go @@ -697,8 +697,6 @@ type ZOSVersionRequest struct { SafeToUpgrade bool `json:"safe_to_upgrade" binding:"required"` } -var versionRegex = regexp.MustCompile(`^v\d+\.\d+\.\d+$`) - // @Summary Set ZOS Version // @Description Sets the ZOS version // @Tags ZOS @@ -795,8 +793,10 @@ func ensureOwner(c *gin.Context, twinID uint64) { } } +// addVersionValidato registers a custom validator for version func addVersionValidator() error { - // Register the custom validation + versionRegex := regexp.MustCompile(`^v\d+\.\d+\.\d+$`) + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { if err := v.RegisterValidation("versionfmt", func(fl validator.FieldLevel) bool { version := fl.Field().String() From 5883fc775bc798d144021fe46958388a237f4b49 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Wed, 9 Apr 2025 18:02:25 +0200 Subject: [PATCH 7/7] add fallback for setting version --- node-registrar/client/zos_version.go | 57 +++++++++++++++++++++------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/node-registrar/client/zos_version.go b/node-registrar/client/zos_version.go index cb4a483..249d1a2 100644 --- a/node-registrar/client/zos_version.go +++ b/node-registrar/client/zos_version.go @@ -80,6 +80,26 @@ func (c *RegistrarClient) setZosVersion(v string, safeToUpgrade bool) (err error return errors.Wrap(err, "failed to construct registrar url") } + sendRequest := func(body bytes.Buffer) (resp *http.Response, err error) { + req, err := http.NewRequest("PUT", url, &body) + if err != nil { + return resp, errors.Wrap(err, "failed to construct http request to the registrar") + } + + authHeader, err := c.signRequest(time.Now().Unix()) + if err != nil { + return resp, errors.Wrap(err, "failed to sign request") + } + req.Header.Set("X-Auth", authHeader) + req.Header.Set("Content-Type", "application/json") + + resp, err = c.httpClient.Do(req) + if err != nil { + return resp, errors.Wrap(err, "failed to send request to get zos version from the registrar") + } + return resp, nil + } + version := ZosVersion{ Version: v, SafeToUpgrade: safeToUpgrade, @@ -91,24 +111,35 @@ func (c *RegistrarClient) setZosVersion(v string, safeToUpgrade bool) (err error return errors.Wrap(err, "failed to encode request body") } - req, err := http.NewRequest("PUT", url, &body) + resp, err := sendRequest(body) if err != nil { - return errors.Wrap(err, "failed to construct http request to the registrar") + return err } + defer resp.Body.Close() - authHeader, err := c.signRequest(time.Now().Unix()) - if err != nil { - return errors.Wrap(err, "failed to sign request") - } - req.Header.Set("X-Auth", authHeader) - req.Header.Set("Content-Type", "application/json") + if resp.StatusCode == http.StatusBadRequest { + // fallback to old encoded format + jsonData, err := json.Marshal(version) + if err != nil { + return errors.Wrap(err, "failed to marshal zos version") + } - resp, err := c.httpClient.Do(req) - if err != nil { - return errors.Wrap(err, "failed to send request to get zos version from the registrar") - } + encodedVersion := struct { + Version string `json:"version"` + }{ + Version: base64.StdEncoding.EncodeToString(jsonData), + } - defer resp.Body.Close() + jsonData, err = json.Marshal(encodedVersion) + if err != nil { + return errors.Wrap(err, "failed to marshal zos version in hex format") + } + + resp, err = sendRequest(*bytes.NewBuffer(jsonData)) + if err != nil { + return err + } + } if resp.StatusCode != http.StatusOK { return parseResponseError(resp.Body)