Making progress on automated upgrade support via manifest and global upgrades.
This commit is contained in:
parent
3962e71ddd
commit
fe465cf492
|
|
@ -83,15 +83,16 @@ type Cluster struct {
|
||||||
deleteOptions metav1.DeleteOptions
|
deleteOptions metav1.DeleteOptions
|
||||||
podEventsQueue *cache.FIFO
|
podEventsQueue *cache.FIFO
|
||||||
|
|
||||||
teamsAPIClient teams.Interface
|
teamsAPIClient teams.Interface
|
||||||
oauthTokenGetter OAuthTokenGetter
|
oauthTokenGetter OAuthTokenGetter
|
||||||
KubeClient k8sutil.KubernetesClient //TODO: move clients to the better place?
|
KubeClient k8sutil.KubernetesClient //TODO: move clients to the better place?
|
||||||
currentProcess Process
|
currentProcess Process
|
||||||
processMu sync.RWMutex // protects the current operation for reporting, no need to hold the master mutex
|
processMu sync.RWMutex // protects the current operation for reporting, no need to hold the master mutex
|
||||||
specMu sync.RWMutex // protects the spec for reporting, no need to hold the master mutex
|
specMu sync.RWMutex // protects the spec for reporting, no need to hold the master mutex
|
||||||
ConnectionPooler map[PostgresRole]*ConnectionPoolerObjects
|
ConnectionPooler map[PostgresRole]*ConnectionPoolerObjects
|
||||||
EBSVolumes map[string]volumes.VolumeProperties
|
EBSVolumes map[string]volumes.VolumeProperties
|
||||||
VolumeResizer volumes.VolumeResizer
|
VolumeResizer volumes.VolumeResizer
|
||||||
|
currentMajorVersion int
|
||||||
}
|
}
|
||||||
|
|
||||||
type compareStatefulsetResult struct {
|
type compareStatefulsetResult struct {
|
||||||
|
|
@ -781,6 +782,10 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
|
||||||
updateFailed = true
|
updateFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.majorVersionUpgrade(); err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/zalando/postgres-operator/pkg/spec"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VersionMap Map of version numbers
|
||||||
|
var VersionMap = map[string]int{
|
||||||
|
"9.5": 9500,
|
||||||
|
"9.6": 9600,
|
||||||
|
"10": 10000,
|
||||||
|
"11": 11000,
|
||||||
|
"12": 12000,
|
||||||
|
"13": 13000,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBiggerPostgresVersion Compare two Postgres version numbers
|
||||||
|
func IsBiggerPostgresVersion(old string, new string) bool {
|
||||||
|
oldN, _ := VersionMap[old]
|
||||||
|
newN, _ := VersionMap[new]
|
||||||
|
return newN > oldN
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDesiredMajorVersionAsInt Convert string to comparable integer of PG version
|
||||||
|
func (c *Cluster) GetDesiredMajorVersionAsInt() int {
|
||||||
|
return VersionMap[c.GetDesiredMajorVersion()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDesiredMajorVersion returns major version to use, incl. potential auto upgrade
|
||||||
|
func (c *Cluster) GetDesiredMajorVersion() string {
|
||||||
|
|
||||||
|
if c.Config.OpConfig.MajorVersionUpgradeMode == "full" {
|
||||||
|
if IsBiggerPostgresVersion(c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion) {
|
||||||
|
c.logger.Infof("Overwriting configured major version %s to %s", c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion)
|
||||||
|
return c.Config.OpConfig.TargetMajorVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Spec.PgVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) majorVersionUpgrade() error {
|
||||||
|
|
||||||
|
if c.OpConfig.MajorVersionUpgradeMode == "off" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pods, _ := c.listPods()
|
||||||
|
allRunning := true
|
||||||
|
|
||||||
|
var masterPod *v1.Pod
|
||||||
|
|
||||||
|
for _, pod := range pods {
|
||||||
|
ps, _ := c.patroni.GetMemberData(&pod)
|
||||||
|
|
||||||
|
if ps.State != "running" {
|
||||||
|
allRunning = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ps.Role == "master" {
|
||||||
|
masterPod = &pod
|
||||||
|
c.currentMajorVersion = ps.ServerVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numberOfPods := len(pods)
|
||||||
|
if allRunning && masterPod != nil {
|
||||||
|
if c.currentMajorVersion < c.GetDesiredMajorVersionAsInt() {
|
||||||
|
podName := &spec.NamespacedName{Namespace: masterPod.Namespace, Name: masterPod.Name}
|
||||||
|
c.ExecCommand(podName, fmt.Sprintf("python3 /scripts/inplace_upgrade.py %d", numberOfPods))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) getCurrentMajorVersion() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -480,3 +480,45 @@ func TestInfrastructureRoleDefinitions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubConfig struct {
|
||||||
|
teammap map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SuperConfig struct {
|
||||||
|
sub SubConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnderstandingMapsAndReferences(t *testing.T) {
|
||||||
|
teams := map[string]string{"acid": "Felix"}
|
||||||
|
|
||||||
|
sc := SubConfig{
|
||||||
|
teammap: teams,
|
||||||
|
}
|
||||||
|
|
||||||
|
ssc := SuperConfig{
|
||||||
|
sub: sc,
|
||||||
|
}
|
||||||
|
|
||||||
|
teams["24x7"] = "alex"
|
||||||
|
|
||||||
|
if len(ssc.sub.teammap) != 2 {
|
||||||
|
t.Errorf("Team Map does not contain 2 elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
ssc.sub.teammap["teapot"] = "Mikkel"
|
||||||
|
|
||||||
|
if len(teams) != 3 {
|
||||||
|
t.Errorf("Team Map does not contain 3 elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
teams = make(map[string]string)
|
||||||
|
|
||||||
|
if len(ssc.sub.teammap) != 3 {
|
||||||
|
t.Errorf("Team Map does not contain 0 elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
if &teams == &(ssc.sub.teammap) {
|
||||||
|
t.Errorf("Identical maps")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,10 @@ type Config struct {
|
||||||
EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"`
|
EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"`
|
||||||
EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"`
|
EnablePgVersionEnvVar bool `name:"enable_pgversion_env_var" default:"true"`
|
||||||
EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"`
|
EnableSpiloWalPathCompat bool `name:"enable_spilo_wal_path_compat" default:"false"`
|
||||||
|
MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade
|
||||||
|
MinimalMajorVersion string `name:"minimal_major_version" default:"9.5"`
|
||||||
|
TargetMajorVersion string `name:"target_major_version" default:"13"`
|
||||||
|
AllowedMajorUpgradeVersions []string `name:"allowed_major_upgrade_versions" default:"12,13"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustMarshal marshals the config or panics
|
// MustMarshal marshals the config or panics
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ type Interface interface {
|
||||||
Switchover(master *v1.Pod, candidate string) error
|
Switchover(master *v1.Pod, candidate string) error
|
||||||
SetPostgresParameters(server *v1.Pod, options map[string]string) error
|
SetPostgresParameters(server *v1.Pod, options map[string]string) error
|
||||||
GetPatroniMemberState(pod *v1.Pod) (string, error)
|
GetPatroniMemberState(pod *v1.Pod) (string, error)
|
||||||
|
GetMemberData(server *v1.Pod) (MemberData, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patroni API client
|
// Patroni API client
|
||||||
|
|
@ -158,3 +159,56 @@ func (p *Patroni) GetPatroniMemberState(server *v1.Pod) (string, error) {
|
||||||
return state, nil
|
return state, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MemberData Patroni member data from Patroni API
|
||||||
|
type MemberData struct {
|
||||||
|
State string
|
||||||
|
Role string
|
||||||
|
ServerVersion int
|
||||||
|
Scope string
|
||||||
|
PatroniVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemberData read member data from patroni API
|
||||||
|
func (p *Patroni) GetMemberData(server *v1.Pod) (MemberData, error) {
|
||||||
|
|
||||||
|
apiURLString, err := apiURL(server)
|
||||||
|
if err != nil {
|
||||||
|
return MemberData{}, err
|
||||||
|
}
|
||||||
|
response, err := p.httpClient.Get(apiURLString)
|
||||||
|
if err != nil {
|
||||||
|
return MemberData{}, fmt.Errorf("could not perform Get request: %v", err)
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return MemberData{}, fmt.Errorf("could not read response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(body, &data)
|
||||||
|
if err != nil {
|
||||||
|
return MemberData{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
memberData := MemberData{}
|
||||||
|
|
||||||
|
var ok, r bool
|
||||||
|
|
||||||
|
memberData.state, r = data["state"].(string)
|
||||||
|
ok = ok && r
|
||||||
|
memberData.serverVersion, r = data["server_version"].(int)
|
||||||
|
ok = ok && r
|
||||||
|
memberData.role, r = data["role"].(string)
|
||||||
|
ok = ok && r
|
||||||
|
memberData.role, r = data["scope"].(string)
|
||||||
|
ok = ok && r
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return MemberData{}, errors.New("Patroni member data could not be read")
|
||||||
|
}
|
||||||
|
|
||||||
|
return memberData, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue