From 1a5090de53497fb252a27bb3ca4f1b8aaf3f7d52 Mon Sep 17 00:00:00 2001 From: rawdaGastan Date: Tue, 10 Sep 2024 16:08:35 +0300 Subject: [PATCH 1/2] add rollout configs for zos update --- cmds/identityd/main.go | 5 ++-- pkg/upgrade/README.md | 4 +-- pkg/upgrade/config.json | 18 +++++++++++++ pkg/upgrade/rollout.go | 60 +++++++++++++++++++++++++++++++++++++++++ pkg/upgrade/upgrade.go | 23 +++++++++++++--- 5 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 pkg/upgrade/config.json create mode 100644 pkg/upgrade/rollout.go diff --git a/cmds/identityd/main.go b/cmds/identityd/main.go index bd1156868..67b612fc7 100644 --- a/cmds/identityd/main.go +++ b/cmds/identityd/main.go @@ -68,8 +68,9 @@ func main() { version.ShowAndExit(false) } + env := environment.MustGet() + if farm { - env := environment.MustGet() fmt.Println(env.FarmID) os.Exit(0) } else if net { @@ -107,7 +108,7 @@ func main() { log.Fatal().Err(err).Msg("failed to create identity manager") } - upgrader, err := upgrade.NewUpgrader(root, upgrade.NoZosUpgrade(debug)) + upgrader, err := upgrade.NewUpgrader(root, uint32(env.FarmID), upgrade.NoZosUpgrade(debug)) if err != nil { log.Fatal().Err(err).Msg("failed to initialize upgrader") } diff --git a/pkg/upgrade/README.md b/pkg/upgrade/README.md index 1af223222..669b07b28 100644 --- a/pkg/upgrade/README.md +++ b/pkg/upgrade/README.md @@ -27,14 +27,14 @@ Then run the upgrader `upgrader.Run(ctx)` ## How it works -The upgrader module has two running modes depeding on the booting method. +The upgrader module has two running modes depending on the booting method. ### Bootstrap Method Running the upgrader on a node run with `bootstrap` will periodically check the hub for latest tag, and if that tag differs from the current one, it updates the local packages to latest. -If the update failed, the upgrader would attempts to install the packages again every `10 secounds` until all packages are successfully updated to prevent partial updates. +If the update failed, the upgrader would attempts to install the packages again every `10 seconds` until all packages are successfully updated to prevent partial updates. The upgrader runs periodically every hour to check for new updates. diff --git a/pkg/upgrade/config.json b/pkg/upgrade/config.json new file mode 100644 index 000000000..9c6188951 --- /dev/null +++ b/pkg/upgrade/config.json @@ -0,0 +1,18 @@ +{ + "development": { + "test_farms": [73], + "safe_to_upgrade": false + }, + "qa": { + "test_farms": [73], + "safe_to_upgrade": false + }, + "testing": { + "test_farms": [73], + "safe_to_upgrade": false + }, + "production": { + "test_farms": [73], + "safe_to_upgrade": false + } +} diff --git a/pkg/upgrade/rollout.go b/pkg/upgrade/rollout.go new file mode 100644 index 000000000..18eefab69 --- /dev/null +++ b/pkg/upgrade/rollout.go @@ -0,0 +1,60 @@ +package upgrade + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/threefoldtech/zos/pkg/environment" +) + +const defaultRolloutConfigURL = "https://raw.githubusercontent.com/threefoldtech/zos/development_rollout_update/pkg/upgrade/config.json" + +// RolloutConfig is the configuration for A/B testing before zos update +type RolloutConfig struct { + TestFarms []uint32 `json:"test_farms"` + SafeToUpgrade bool `json:"safe_to_upgrade"` +} + +func parseRolloutConfig(file io.Reader) (map[string]RolloutConfig, error) { + var conf map[string]RolloutConfig + + configFile, err := io.ReadAll(file) + if err != nil { + return nil, fmt.Errorf("failed to read the rollout config file: %+w", err) + } + + if err = json.Unmarshal(configFile, &conf); err != nil { + return nil, err + } + + for network := range conf { + if network != environment.RunningDev.String() && network != environment.RunningQA.String() && network != environment.RunningTest.String() && network != environment.RunningMain.String() { + return nil, fmt.Errorf("invalid network passed: %s", network) + } + } + + return conf, nil +} + +func readRolloutConfig(env string) (RolloutConfig, error) { + response, err := http.Get(defaultRolloutConfigURL) + if err != nil { + return RolloutConfig{}, err + } + + defer response.Body.Close() + + rolloutConfig, err := parseRolloutConfig(response.Body) + if err != nil { + return RolloutConfig{}, err + } + + cfg, ok := rolloutConfig[env] + if !ok { + return RolloutConfig{}, fmt.Errorf("env '%s' does not exist in the configs", env) + } + + return cfg, nil +} diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go index 7f2f75398..3c901916e 100644 --- a/pkg/upgrade/upgrade.go +++ b/pkg/upgrade/upgrade.go @@ -57,6 +57,7 @@ type Upgrader struct { noZosUpgrade bool hub *hub.HubClient storage storage.Storage + farmID uint32 } // UpgraderOption interface @@ -96,11 +97,12 @@ func Zinit(socket string) UpgraderOption { } // NewUpgrader creates a new upgrader instance -func NewUpgrader(root string, opts ...UpgraderOption) (*Upgrader, error) { +func NewUpgrader(root string, farmID uint32, opts ...UpgraderOption) (*Upgrader, error) { hubClient := hub.NewHubClient(defaultHubTimeout) u := &Upgrader{ - root: root, - hub: hubClient, + root: root, + farmID: farmID, + hub: hubClient, } for _, dir := range []string{u.fileCache(), u.flistCache()} { @@ -131,7 +133,7 @@ func NewUpgrader(root string, opts ...UpgraderOption) (*Upgrader, error) { return u, nil } -// Run strats the upgrader module and run the update according to the detected boot method +// Run starts the upgrader module and run the update according to the detected boot method func (u *Upgrader) Run(ctx context.Context) error { method := u.boot.DetectBootMethod() if method == BootMethodOther { @@ -235,6 +237,19 @@ func (u *Upgrader) update() error { return nil } + rolloutConfigs, err := readRolloutConfig(u.boot.RunMode().String()) + if err != nil { + return errors.Wrap(err, "failed to read rollout configs") + } + + if !rolloutConfigs.SafeToUpgrade { + if !slices.Contains(rolloutConfigs.TestFarms, u.farmID) { + // nothing to do! waiting for the flag `safe to upgrade to be enabled after A/B testing` + // node is not a part of A/B testing + return nil + } + } + log.Info().Str("version", filepath.Base(remote.Target)).Msg("updating system...") if err := u.updateTo(remote, ¤t); err != nil { return errors.Wrapf(err, "failed to update to new tag '%s'", remote.Target) From 1cab7cb67c6c47c72bfbd0e972f0f503ba8be05e Mon Sep 17 00:00:00 2001 From: rawdaGastan Date: Wed, 18 Sep 2024 16:31:37 +0300 Subject: [PATCH 2/2] use zos configs --- cmds/identityd/main.go | 5 ++-- pkg/environment/config.go | 4 +++ pkg/upgrade/config.json | 18 ------------ pkg/upgrade/rollout.go | 60 --------------------------------------- pkg/upgrade/upgrade.go | 18 ++++++------ 5 files changed, 16 insertions(+), 89 deletions(-) delete mode 100644 pkg/upgrade/config.json delete mode 100644 pkg/upgrade/rollout.go diff --git a/cmds/identityd/main.go b/cmds/identityd/main.go index 67b612fc7..bd1156868 100644 --- a/cmds/identityd/main.go +++ b/cmds/identityd/main.go @@ -68,9 +68,8 @@ func main() { version.ShowAndExit(false) } - env := environment.MustGet() - if farm { + env := environment.MustGet() fmt.Println(env.FarmID) os.Exit(0) } else if net { @@ -108,7 +107,7 @@ func main() { log.Fatal().Err(err).Msg("failed to create identity manager") } - upgrader, err := upgrade.NewUpgrader(root, uint32(env.FarmID), upgrade.NoZosUpgrade(debug)) + upgrader, err := upgrade.NewUpgrader(root, upgrade.NoZosUpgrade(debug)) if err != nil { log.Fatal().Err(err).Msg("failed to initialize upgrader") } diff --git a/pkg/environment/config.go b/pkg/environment/config.go index 9a0595ff8..7c51d999c 100644 --- a/pkg/environment/config.go +++ b/pkg/environment/config.go @@ -27,6 +27,10 @@ type Config struct { Users struct { Authorized []string `json:"authorized"` } `json:"users"` + RolloutUpgrade struct { + TestFarms []uint32 `json:"test_farms"` + SafeToUpgrade bool `json:"safe_to_upgrade"` + } `json:"rollout_upgrade"` } // Merge, updates current config with cfg merging and override config diff --git a/pkg/upgrade/config.json b/pkg/upgrade/config.json deleted file mode 100644 index 9c6188951..000000000 --- a/pkg/upgrade/config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "development": { - "test_farms": [73], - "safe_to_upgrade": false - }, - "qa": { - "test_farms": [73], - "safe_to_upgrade": false - }, - "testing": { - "test_farms": [73], - "safe_to_upgrade": false - }, - "production": { - "test_farms": [73], - "safe_to_upgrade": false - } -} diff --git a/pkg/upgrade/rollout.go b/pkg/upgrade/rollout.go deleted file mode 100644 index 18eefab69..000000000 --- a/pkg/upgrade/rollout.go +++ /dev/null @@ -1,60 +0,0 @@ -package upgrade - -import ( - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/threefoldtech/zos/pkg/environment" -) - -const defaultRolloutConfigURL = "https://raw.githubusercontent.com/threefoldtech/zos/development_rollout_update/pkg/upgrade/config.json" - -// RolloutConfig is the configuration for A/B testing before zos update -type RolloutConfig struct { - TestFarms []uint32 `json:"test_farms"` - SafeToUpgrade bool `json:"safe_to_upgrade"` -} - -func parseRolloutConfig(file io.Reader) (map[string]RolloutConfig, error) { - var conf map[string]RolloutConfig - - configFile, err := io.ReadAll(file) - if err != nil { - return nil, fmt.Errorf("failed to read the rollout config file: %+w", err) - } - - if err = json.Unmarshal(configFile, &conf); err != nil { - return nil, err - } - - for network := range conf { - if network != environment.RunningDev.String() && network != environment.RunningQA.String() && network != environment.RunningTest.String() && network != environment.RunningMain.String() { - return nil, fmt.Errorf("invalid network passed: %s", network) - } - } - - return conf, nil -} - -func readRolloutConfig(env string) (RolloutConfig, error) { - response, err := http.Get(defaultRolloutConfigURL) - if err != nil { - return RolloutConfig{}, err - } - - defer response.Body.Close() - - rolloutConfig, err := parseRolloutConfig(response.Body) - if err != nil { - return RolloutConfig{}, err - } - - cfg, ok := rolloutConfig[env] - if !ok { - return RolloutConfig{}, fmt.Errorf("env '%s' does not exist in the configs", env) - } - - return cfg, nil -} diff --git a/pkg/upgrade/upgrade.go b/pkg/upgrade/upgrade.go index 3c901916e..8444b2cc3 100644 --- a/pkg/upgrade/upgrade.go +++ b/pkg/upgrade/upgrade.go @@ -20,6 +20,7 @@ import ( "github.com/threefoldtech/0-fs/rofs" "github.com/threefoldtech/0-fs/storage" "github.com/threefoldtech/zos/pkg/app" + "github.com/threefoldtech/zos/pkg/environment" "github.com/threefoldtech/zos/pkg/upgrade/hub" "github.com/threefoldtech/zos/pkg/zinit" @@ -57,7 +58,6 @@ type Upgrader struct { noZosUpgrade bool hub *hub.HubClient storage storage.Storage - farmID uint32 } // UpgraderOption interface @@ -97,12 +97,11 @@ func Zinit(socket string) UpgraderOption { } // NewUpgrader creates a new upgrader instance -func NewUpgrader(root string, farmID uint32, opts ...UpgraderOption) (*Upgrader, error) { +func NewUpgrader(root string, opts ...UpgraderOption) (*Upgrader, error) { hubClient := hub.NewHubClient(defaultHubTimeout) u := &Upgrader{ - root: root, - farmID: farmID, - hub: hubClient, + root: root, + hub: hubClient, } for _, dir := range []string{u.fileCache(), u.flistCache()} { @@ -237,13 +236,16 @@ func (u *Upgrader) update() error { return nil } - rolloutConfigs, err := readRolloutConfig(u.boot.RunMode().String()) + env := environment.MustGet() + config, err := environment.GetConfig() if err != nil { - return errors.Wrap(err, "failed to read rollout configs") + return errors.Wrap(err, "failed to get network config") } + rolloutConfigs := config.RolloutUpgrade + if !rolloutConfigs.SafeToUpgrade { - if !slices.Contains(rolloutConfigs.TestFarms, u.farmID) { + if !slices.Contains(rolloutConfigs.TestFarms, uint32(env.FarmID)) { // nothing to do! waiting for the flag `safe to upgrade to be enabled after A/B testing` // node is not a part of A/B testing return nil