1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Bugfix] Fix 3.6 -> 3.7 upgrade (#910)

This commit is contained in:
Adam Janikowski 2022-02-16 01:36:45 +01:00 committed by GitHub
parent 1e9f226841
commit dc43d671e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 49 deletions

View file

@ -10,6 +10,8 @@
- (Cleanup) Reorganize main reconciliation context
- (Bugfix) Unreachable condition
- (Feature) Allow to disable external port (sidecar managed connection)
- (Bugfix) Fix 3.6 -> 3.7 Upgrade procedure
- (Bugfix) Add missing finalizer
## [1.2.7](https://github.com/arangodb/kube-arangodb/tree/1.2.7) (2022-01-17)
- Add Plan BackOff functionality

View file

@ -289,37 +289,20 @@ func (d *Deployment) getAuth() (driver.Authentication, error) {
}
var secret string
if i := d.apiObject.Status.CurrentImage; i == nil || !features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), d.apiObject.Spec.Authentication.GetJWTSecretName(), meta.GetOptions{})
if err != nil {
return nil, errors.Newf("JWT Secret is missing")
}
var found bool
jwt, ok := s.Data[constants.SecretKeyToken]
if !ok {
return nil, errors.Newf("JWT Secret is invalid")
}
// Check if we can find token in folder
if i := d.apiObject.Status.CurrentImage; i == nil || features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
secret, found = d.getJWTFolderToken()
}
secret = string(jwt)
} else {
s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), pod.JWTSecretFolder(d.GetName()), meta.GetOptions{})
if err != nil {
d.deps.Log.Error().Err(err).Msgf("Unable to get secret")
return nil, errors.Newf("JWT Folder Secret is missing")
}
// Fallback to token
if !found {
secret, found = d.getJWTToken()
}
if len(s.Data) == 0 {
return nil, errors.Newf("JWT Folder Secret is empty")
}
if q, ok := s.Data[pod.ActiveJWTKey]; ok {
secret = string(q)
} else {
for _, q := range s.Data {
secret = string(q)
break
}
}
if !found {
return nil, errors.Newf("JWT Secret is invalid")
}
jwt, err := jwt.CreateArangodJwtAuthorizationHeader(secret, "kube-arangodb")
@ -330,6 +313,44 @@ func (d *Deployment) getAuth() (driver.Authentication, error) {
return driver.RawAuthentication(jwt), nil
}
func (d *Deployment) getJWTFolderToken() (string, bool) {
if i := d.apiObject.Status.CurrentImage; i == nil || features.JWTRotation().Supported(i.ArangoDBVersion, i.Enterprise) {
s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), pod.JWTSecretFolder(d.GetName()), meta.GetOptions{})
if err != nil {
d.deps.Log.Error().Err(err).Msgf("Unable to get secret")
return "", false
}
if len(s.Data) == 0 {
return "", false
}
if q, ok := s.Data[pod.ActiveJWTKey]; ok {
return string(q), true
} else {
for _, q := range s.Data {
return string(q), true
}
}
}
return "", false
}
func (d *Deployment) getJWTToken() (string, bool) {
s, err := d.GetCachedStatus().SecretReadInterface().Get(context.Background(), d.apiObject.Spec.Authentication.GetJWTSecretName(), meta.GetOptions{})
if err != nil {
return "", false
}
jwt, ok := s.Data[constants.SecretKeyToken]
if !ok {
return "", false
}
return string(jwt), true
}
// GetSyncServerClient returns a cached client for a specific arangosync server.
func (d *Deployment) GetSyncServerClient(ctx context.Context, group api.ServerGroup, id string) (client.API, error) {
// Fetch monitoring token

View file

@ -474,13 +474,17 @@ func (d *Deployment) updateCRStatus(ctx context.Context, force ...bool) error {
attempt := 0
for {
attempt++
q := patch.NewPatch(patch.ItemReplace(patch.NewPath("status"), d.status.last))
if d.apiObject.GetDeletionTimestamp() == nil {
ensureFinalizers(d.apiObject)
if ensureFinalizers(d.apiObject) {
q.ItemAdd(patch.NewPath("metadata", "finalizers"), d.apiObject.Finalizers)
}
}
var newAPIObject *api.ArangoDeployment
err := globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
p, err := patch.NewPatch(patch.ItemReplace(patch.NewPath("status"), d.status.last)).Marshal()
p, err := q.Marshal()
if err != nil {
return err
}

View file

@ -39,15 +39,17 @@ import (
)
// ensureFinalizers adds all required finalizers to the given deployment (in memory).
func ensureFinalizers(depl *api.ArangoDeployment) {
func ensureFinalizers(depl *api.ArangoDeployment) bool {
for _, f := range depl.GetFinalizers() {
if f == constants.FinalizerDeplRemoveChildFinalizers {
// Finalizer already set
return
return false
}
}
// Set finalizers
depl.SetFinalizers(append(depl.GetFinalizers(), constants.FinalizerDeplRemoveChildFinalizers))
return true
}
// runDeploymentFinalizers goes through the list of ArangoDeployoment finalizers to see if they can be removed.

View file

@ -24,6 +24,7 @@ import (
"context"
"sync"
"github.com/arangodb/go-driver"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/reconciler"
"github.com/arangodb/kube-arangodb/pkg/util/globals"
@ -80,9 +81,11 @@ func (s *stateInspector) RefreshState(ctx context.Context, members api.Deploymen
return
}
if _, err := c.Version(nctx); err != nil {
if v, err := c.Version(nctx); err != nil {
results[id].Reachable = err
return
} else {
results[id].Version = v
}
})
@ -110,6 +113,8 @@ func (s *stateInspector) MemberState(id string) (State, bool) {
type State struct {
Reachable error
Version driver.VersionInfo
}
func (s State) IsReachable() bool {

View file

@ -28,6 +28,7 @@ import (
"github.com/rs/zerolog/log"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/rs/zerolog"
)
@ -86,6 +87,7 @@ func (a *actionArangoMemberUpdatePodSpec) Start(ctx context.Context) (bool, erro
if m.Endpoint == nil || *m.Endpoint != endpoint {
// Update endpoint
m.Endpoint = util.NewString(endpoint)
if err := status.Members.Update(m, a.action.Group); err != nil {
log.Error().Err(err).Msg("Unable to update endpoint")
return false, err

View file

@ -23,13 +23,13 @@ package reconcile
import (
"context"
"github.com/arangodb/go-driver"
"github.com/arangodb/kube-arangodb/pkg/deployment/rotation"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
"github.com/arangodb/go-driver"
upgraderules "github.com/arangodb/go-upgrade-rules"
"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -119,6 +119,26 @@ func createRotateOrUpgradePlanInternal(log zerolog.Logger, apiObject k8sutil.API
decision := createRotateOrUpgradeDecision(log, spec, status, context)
if decision.IsUpgrade() {
for _, m := range status.Members.AsList() {
// Pre-check
d := decision[m.Member.ID]
if !d.upgrade {
continue
}
// We have member to upgrade
if d.upgradeDecision.Hold {
// Holding upgrade
continue
}
if !d.upgradeDecision.UpgradeAllowed {
context.CreateEvent(k8sutil.NewUpgradeNotAllowedEvent(apiObject, d.upgradeDecision.FromVersion, d.upgradeDecision.ToVersion, d.upgradeDecision.FromLicense, d.upgradeDecision.ToLicense))
return nil, false
}
}
// Upgrade phase
// During upgrade always get first member which needs to be upgraded
for _, m := range status.Members.AsList() {
@ -175,7 +195,7 @@ func createRotateOrUpgradePlanInternal(log zerolog.Logger, apiObject k8sutil.API
}
if m.Member.Conditions.IsTrue(api.ConditionTypeRestart) {
return createRotateMemberPlan(log, m.Member, m.Group, "Restart flag present"), false
return createRotateMemberPlan(log, m.Member, m.Group, spec, "Restart flag present"), false
}
arangoMember, ok := cachedStatus.ArangoMember(m.Member.ArangoMemberName(apiObject.GetName(), m.Group))
if !ok {
@ -402,18 +422,15 @@ func createUpgradeMemberPlan(log zerolog.Logger, member api.MemberStatus,
Str("reason", reason).
Str("action", string(upgradeAction)).
Msg("Creating upgrade plan")
var plan = api.Plan{
api.NewAction(api.ActionTypeCleanTLSKeyfileCertificate, group, member.ID, "Remove server keyfile and enforce renewal/recreation"),
plan := createRotateMemberPlanWithAction(member, group, upgradeAction, spec, reason)
if member.Image == nil || member.Image.Image != spec.GetImage() {
plan = plan.Before(api.NewAction(api.ActionTypeSetMemberCurrentImage, group, member.ID, reason).SetImage(spec.GetImage()))
}
if status.CurrentImage == nil || status.CurrentImage.Image != spec.GetImage() {
plan = plan.After(api.NewAction(api.ActionTypeSetCurrentImage, group, "", reason).SetImage(spec.GetImage()))
plan = plan.Before(api.NewAction(api.ActionTypeSetCurrentImage, group, "", reason).SetImage(spec.GetImage()))
}
if member.Image == nil || member.Image.Image != spec.GetImage() {
plan = plan.After(api.NewAction(api.ActionTypeSetMemberCurrentImage, group, member.ID, reason).SetImage(spec.GetImage()))
}
plan = plan.After(api.NewAction(upgradeAction, group, member.ID, reason),
api.NewAction(api.ActionTypeWaitForMemberUp, group, member.ID))
return withSecureWrap(member, group, spec, plan...)
}

View file

@ -34,20 +34,32 @@ import (
// createRotateMemberPlan creates a plan to rotate (stop-recreate-start) an existing
// member.
func createRotateMemberPlan(log zerolog.Logger, member api.MemberStatus,
group api.ServerGroup, reason string) api.Plan {
group api.ServerGroup, spec api.DeploymentSpec, reason string) api.Plan {
log.Debug().
Str("id", member.ID).
Str("role", group.AsRole()).
Str("reason", reason).
Msg("Creating rotation plan")
plan := api.Plan{
return createRotateMemberPlanWithAction(member, group, api.ActionTypeRotateMember, spec, reason)
}
// createRotateMemberPlanWithAction creates a plan to rotate (stop-<action>>-start) an existing
// member.
func createRotateMemberPlanWithAction(member api.MemberStatus,
group api.ServerGroup, action api.ActionType, spec api.DeploymentSpec, reason string) api.Plan {
var plan = api.Plan{
api.NewAction(api.ActionTypeCleanTLSKeyfileCertificate, group, member.ID, "Remove server keyfile and enforce renewal/recreation"),
api.NewAction(api.ActionTypeResignLeadership, group, member.ID, reason),
}
plan = withSecureWrap(member, group, spec, plan...)
plan = plan.After(
withMemberPodUID(member, api.NewAction(api.ActionTypeKillMemberPod, group, member.ID, reason)),
withMemberPodUID(member, api.NewAction(api.ActionTypeRotateMember, group, member.ID, reason)),
withMemberPodUID(member, api.NewAction(action, group, member.ID, reason)),
api.NewAction(api.ActionTypeWaitForMemberUp, group, member.ID),
api.NewAction(api.ActionTypeWaitForMemberInSync, group, member.ID),
}
)
return plan
}