mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
GT-134 Add annotation to change architecture (#1107)
This commit is contained in:
parent
2a11165f8e
commit
effed19d53
25 changed files with 325 additions and 26 deletions
|
@ -3,6 +3,7 @@
|
|||
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
|
||||
- (Feature) Add action progress
|
||||
- (Feature) Ensure consistency during replication cancellation
|
||||
- (Feature) Add annotation to change architecture of a member
|
||||
|
||||
## [1.2.19](https://github.com/arangodb/kube-arangodb/tree/1.2.19) (2022-10-05)
|
||||
- (Bugfix) Prevent changes when UID is wrong
|
||||
|
|
20
docs/design/arch_change.md
Normal file
20
docs/design/arch_change.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Member Architecture change
|
||||
|
||||
To change manually architecture of specific member, you can use annotation:
|
||||
```bash
|
||||
kubectl annotate pod arango-pod deployment.arangodb.com/arch=arm64
|
||||
```
|
||||
|
||||
It will add to the member status `ArchitectureMismatch` condition, e.g.:
|
||||
```yaml
|
||||
- lastTransitionTime: "2022-09-15T07:38:05Z"
|
||||
lastUpdateTime: "2022-09-15T07:38:05Z"
|
||||
reason: Member has a different architecture than the deployment
|
||||
status: "True"
|
||||
type: ArchitectureMismatch
|
||||
```
|
||||
|
||||
To provide requested arch changes for the member we need rotate it, so additional step is required:
|
||||
```bash
|
||||
`kubectl annotate pod arango-pod deployment.arangodb.com/rotate=true`
|
||||
```
|
|
@ -60,6 +60,7 @@
|
|||
| SetCondition | 10m0s | Community & Enterprise | (Deprecated) Set deployment condition |
|
||||
| SetConditionV2 | 10m0s | Community & Enterprise | Set deployment condition |
|
||||
| SetCurrentImage | 6h0m0s | Community & Enterprise | Update deployment current image after image discovery |
|
||||
| SetCurrentMemberArch | 10m0s | Community & Enterprise | Set current member architecture |
|
||||
| SetMaintenanceCondition | 10m0s | Community & Enterprise | Update ArangoDB maintenance condition |
|
||||
| SetMemberCondition | 10m0s | Community & Enterprise | (Deprecated) Set member condition |
|
||||
| SetMemberConditionV2 | 10m0s | Community & Enterprise | Set member condition |
|
||||
|
@ -141,6 +142,7 @@ spec:
|
|||
SetCondition: 10m0s
|
||||
SetConditionV2: 10m0s
|
||||
SetCurrentImage: 6h0m0s
|
||||
SetCurrentMemberArch: 10m0s
|
||||
SetMaintenanceCondition: 10m0s
|
||||
SetMemberCondition: 10m0s
|
||||
SetMemberConditionV2: 10m0s
|
||||
|
|
|
@ -84,6 +84,9 @@ actions:
|
|||
enterprise: true
|
||||
description: Update certificate in SNI
|
||||
timeout: 10m
|
||||
SetCurrentMemberArch:
|
||||
description: Set current member architecture
|
||||
timeout: 10m
|
||||
SetCurrentImage:
|
||||
description: Update deployment current image after image discovery
|
||||
timeout: 6h
|
||||
|
|
|
@ -23,6 +23,7 @@ package deployment
|
|||
const (
|
||||
ArangoDeploymentAnnotationPrefix = "deployment.arangodb.com"
|
||||
ArangoDeploymentPodMaintenanceAnnotation = ArangoDeploymentAnnotationPrefix + "/maintenance"
|
||||
ArangoDeploymentPodChangeArchAnnotation = ArangoDeploymentAnnotationPrefix + "/arch"
|
||||
ArangoDeploymentPodRotateAnnotation = ArangoDeploymentAnnotationPrefix + "/rotate"
|
||||
ArangoDeploymentPodReplaceAnnotation = ArangoDeploymentAnnotationPrefix + "/replace"
|
||||
ArangoDeploymentPodDeleteNow = ArangoDeploymentAnnotationPrefix + "/delete_now"
|
||||
|
|
|
@ -137,6 +137,8 @@ const (
|
|||
ActionSetConditionV2DefaultTimeout time.Duration = ActionsDefaultTimeout
|
||||
// ActionSetCurrentImageDefaultTimeout define default timeout for action ActionSetCurrentImage
|
||||
ActionSetCurrentImageDefaultTimeout time.Duration = 21600 * time.Second // 6h0m0s
|
||||
// ActionSetCurrentMemberArchDefaultTimeout define default timeout for action ActionSetCurrentMemberArch
|
||||
ActionSetCurrentMemberArchDefaultTimeout time.Duration = 600 * time.Second // 10m0s
|
||||
// ActionSetMaintenanceConditionDefaultTimeout define default timeout for action ActionSetMaintenanceCondition
|
||||
ActionSetMaintenanceConditionDefaultTimeout time.Duration = ActionsDefaultTimeout
|
||||
// ActionSetMemberConditionDefaultTimeout define default timeout for action ActionSetMemberCondition
|
||||
|
@ -286,6 +288,8 @@ const (
|
|||
ActionTypeSetConditionV2 ActionType = "SetConditionV2"
|
||||
// ActionTypeSetCurrentImage in scopes Normal. Update deployment current image after image discovery
|
||||
ActionTypeSetCurrentImage ActionType = "SetCurrentImage"
|
||||
// ActionTypeSetCurrentMemberArch in scopes Normal. Set current member architecture
|
||||
ActionTypeSetCurrentMemberArch ActionType = "SetCurrentMemberArch"
|
||||
// ActionTypeSetMaintenanceCondition in scopes Normal. Update ArangoDB maintenance condition
|
||||
ActionTypeSetMaintenanceCondition ActionType = "SetMaintenanceCondition"
|
||||
// ActionTypeSetMemberCondition in scopes High. (Deprecated) Set member condition
|
||||
|
@ -436,6 +440,8 @@ func ActionDefaultTimeout(in ActionType) time.Duration {
|
|||
return ActionSetConditionV2DefaultTimeout
|
||||
case ActionTypeSetCurrentImage:
|
||||
return ActionSetCurrentImageDefaultTimeout
|
||||
case ActionTypeSetCurrentMemberArch:
|
||||
return ActionSetCurrentMemberArchDefaultTimeout
|
||||
case ActionTypeSetMaintenanceCondition:
|
||||
return ActionSetMaintenanceConditionDefaultTimeout
|
||||
case ActionTypeSetMemberCondition:
|
||||
|
@ -589,6 +595,8 @@ func GetActionPriority(in ActionType) ActionPriority {
|
|||
return ActionPriorityHigh
|
||||
case ActionTypeSetCurrentImage:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetCurrentMemberArch:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetMaintenanceCondition:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetMemberCondition:
|
||||
|
|
|
@ -49,6 +49,16 @@ func (a ArangoDeploymentArchitecture) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a ArangoDeploymentArchitecture) IsArchAllowed(arch ArangoDeploymentArchitectureType) bool {
|
||||
for id := range a {
|
||||
if a[id] == arch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type ArangoDeploymentArchitectureType string
|
||||
|
||||
const (
|
||||
|
@ -93,7 +103,22 @@ func (a ArangoDeploymentArchitectureType) AsNodeSelectorRequirement() core.NodeS
|
|||
}
|
||||
}
|
||||
|
||||
func GetArchsFromNodeSelector(selectors []core.NodeSelectorTerm) map[ArangoDeploymentArchitectureType]bool {
|
||||
func (a ArangoDeploymentArchitectureType) IsArchMismatch(deploymentArch ArangoDeploymentArchitecture, memberArch *ArangoDeploymentArchitectureType) bool {
|
||||
if a.Validate() != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if deploymentArch.IsArchAllowed(a) {
|
||||
if memberArch == nil {
|
||||
return true
|
||||
} else if a != *memberArch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetAllArchFromNodeSelector(selectors []core.NodeSelectorTerm) map[ArangoDeploymentArchitectureType]bool {
|
||||
result := make(map[ArangoDeploymentArchitectureType]bool)
|
||||
for _, selector := range selectors {
|
||||
if selector.MatchExpressions != nil {
|
||||
|
|
|
@ -74,6 +74,8 @@ const (
|
|||
ConditionTypeScaleDownCandidate ConditionType = "ScaleDownCandidate"
|
||||
// ConditionTypeUpgradeFailed indicates that upgrade failed
|
||||
ConditionTypeUpgradeFailed ConditionType = "UpgradeFailed"
|
||||
// ConditionTypeArchitectureMismatch indicates that the member has a different architecture than the deployment.
|
||||
ConditionTypeArchitectureMismatch ConditionType = "ArchitectureMismatch"
|
||||
|
||||
// ConditionTypeMemberMaintenanceMode indicates that Maintenance is enabled on particular member
|
||||
ConditionTypeMemberMaintenanceMode ConditionType = "MemberMaintenanceMode"
|
||||
|
|
|
@ -97,6 +97,8 @@ type Action struct {
|
|||
Locals PlanLocals `json:"locals,omitempty"`
|
||||
// ID reference of the task involved in this action (if any)
|
||||
TaskID types.UID `json:"taskID,omitempty"`
|
||||
// Architecture of the member involved in this action (if any)
|
||||
Architecture ArangoDeploymentArchitectureType `json:"arch,omitempty"`
|
||||
// Progress describes what is a status of the current action.
|
||||
Progress string `json:"progress,omitempty"`
|
||||
}
|
||||
|
@ -115,6 +117,7 @@ func (a Action) Equal(other Action) bool {
|
|||
equality.Semantic.DeepEqual(a.Params, other.Params) &&
|
||||
a.Locals.Equal(other.Locals) &&
|
||||
a.TaskID == other.TaskID &&
|
||||
a.Architecture == other.Architecture &&
|
||||
a.Progress == other.Progress
|
||||
}
|
||||
|
||||
|
@ -194,6 +197,12 @@ func (a Action) SetImage(image string) Action {
|
|||
return a
|
||||
}
|
||||
|
||||
// SetArch sets the Architecture field to the given value and returns the modified
|
||||
func (a Action) SetArch(arch ArangoDeploymentArchitectureType) Action {
|
||||
a.Architecture = arch
|
||||
return a
|
||||
}
|
||||
|
||||
// IsStarted returns true if the action has been started already.
|
||||
func (a Action) IsStarted() bool {
|
||||
return !a.StartTime.IsZero()
|
||||
|
|
|
@ -137,6 +137,8 @@ const (
|
|||
ActionSetConditionV2DefaultTimeout time.Duration = ActionsDefaultTimeout
|
||||
// ActionSetCurrentImageDefaultTimeout define default timeout for action ActionSetCurrentImage
|
||||
ActionSetCurrentImageDefaultTimeout time.Duration = 21600 * time.Second // 6h0m0s
|
||||
// ActionSetCurrentMemberArchDefaultTimeout define default timeout for action ActionSetCurrentMemberArch
|
||||
ActionSetCurrentMemberArchDefaultTimeout time.Duration = 600 * time.Second // 10m0s
|
||||
// ActionSetMaintenanceConditionDefaultTimeout define default timeout for action ActionSetMaintenanceCondition
|
||||
ActionSetMaintenanceConditionDefaultTimeout time.Duration = ActionsDefaultTimeout
|
||||
// ActionSetMemberConditionDefaultTimeout define default timeout for action ActionSetMemberCondition
|
||||
|
@ -286,6 +288,8 @@ const (
|
|||
ActionTypeSetConditionV2 ActionType = "SetConditionV2"
|
||||
// ActionTypeSetCurrentImage in scopes Normal. Update deployment current image after image discovery
|
||||
ActionTypeSetCurrentImage ActionType = "SetCurrentImage"
|
||||
// ActionTypeSetCurrentMemberArch in scopes Normal. Set current member architecture
|
||||
ActionTypeSetCurrentMemberArch ActionType = "SetCurrentMemberArch"
|
||||
// ActionTypeSetMaintenanceCondition in scopes Normal. Update ArangoDB maintenance condition
|
||||
ActionTypeSetMaintenanceCondition ActionType = "SetMaintenanceCondition"
|
||||
// ActionTypeSetMemberCondition in scopes High. (Deprecated) Set member condition
|
||||
|
@ -436,6 +440,8 @@ func ActionDefaultTimeout(in ActionType) time.Duration {
|
|||
return ActionSetConditionV2DefaultTimeout
|
||||
case ActionTypeSetCurrentImage:
|
||||
return ActionSetCurrentImageDefaultTimeout
|
||||
case ActionTypeSetCurrentMemberArch:
|
||||
return ActionSetCurrentMemberArchDefaultTimeout
|
||||
case ActionTypeSetMaintenanceCondition:
|
||||
return ActionSetMaintenanceConditionDefaultTimeout
|
||||
case ActionTypeSetMemberCondition:
|
||||
|
@ -589,6 +595,8 @@ func GetActionPriority(in ActionType) ActionPriority {
|
|||
return ActionPriorityHigh
|
||||
case ActionTypeSetCurrentImage:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetCurrentMemberArch:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetMaintenanceCondition:
|
||||
return ActionPriorityNormal
|
||||
case ActionTypeSetMemberCondition:
|
||||
|
|
|
@ -49,6 +49,16 @@ func (a ArangoDeploymentArchitecture) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a ArangoDeploymentArchitecture) IsArchAllowed(arch ArangoDeploymentArchitectureType) bool {
|
||||
for id := range a {
|
||||
if a[id] == arch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type ArangoDeploymentArchitectureType string
|
||||
|
||||
const (
|
||||
|
@ -93,7 +103,22 @@ func (a ArangoDeploymentArchitectureType) AsNodeSelectorRequirement() core.NodeS
|
|||
}
|
||||
}
|
||||
|
||||
func GetArchsFromNodeSelector(selectors []core.NodeSelectorTerm) map[ArangoDeploymentArchitectureType]bool {
|
||||
func (a ArangoDeploymentArchitectureType) IsArchMismatch(deploymentArch ArangoDeploymentArchitecture, memberArch *ArangoDeploymentArchitectureType) bool {
|
||||
if a.Validate() != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if deploymentArch.IsArchAllowed(a) {
|
||||
if memberArch == nil {
|
||||
return true
|
||||
} else if a != *memberArch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetAllArchFromNodeSelector(selectors []core.NodeSelectorTerm) map[ArangoDeploymentArchitectureType]bool {
|
||||
result := make(map[ArangoDeploymentArchitectureType]bool)
|
||||
for _, selector := range selectors {
|
||||
if selector.MatchExpressions != nil {
|
||||
|
|
|
@ -74,6 +74,8 @@ const (
|
|||
ConditionTypeScaleDownCandidate ConditionType = "ScaleDownCandidate"
|
||||
// ConditionTypeUpgradeFailed indicates that upgrade failed
|
||||
ConditionTypeUpgradeFailed ConditionType = "UpgradeFailed"
|
||||
// ConditionTypeArchitectureMismatch indicates that the member has a different architecture than the deployment.
|
||||
ConditionTypeArchitectureMismatch ConditionType = "ArchitectureMismatch"
|
||||
|
||||
// ConditionTypeMemberMaintenanceMode indicates that Maintenance is enabled on particular member
|
||||
ConditionTypeMemberMaintenanceMode ConditionType = "MemberMaintenanceMode"
|
||||
|
|
|
@ -97,6 +97,8 @@ type Action struct {
|
|||
Locals PlanLocals `json:"locals,omitempty"`
|
||||
// ID reference of the task involved in this action (if any)
|
||||
TaskID types.UID `json:"taskID,omitempty"`
|
||||
// Architecture of the member involved in this action (if any)
|
||||
Architecture ArangoDeploymentArchitectureType `json:"arch,omitempty"`
|
||||
// Progress describes what is a status of the current action.
|
||||
Progress string `json:"progress,omitempty"`
|
||||
}
|
||||
|
@ -115,6 +117,7 @@ func (a Action) Equal(other Action) bool {
|
|||
equality.Semantic.DeepEqual(a.Params, other.Params) &&
|
||||
a.Locals.Equal(other.Locals) &&
|
||||
a.TaskID == other.TaskID &&
|
||||
a.Architecture == other.Architecture &&
|
||||
a.Progress == other.Progress
|
||||
}
|
||||
|
||||
|
@ -194,6 +197,12 @@ func (a Action) SetImage(image string) Action {
|
|||
return a
|
||||
}
|
||||
|
||||
// SetArch sets the Architecture field to the given value and returns the modified
|
||||
func (a Action) SetArch(arch ArangoDeploymentArchitectureType) Action {
|
||||
a.Architecture = arch
|
||||
return a
|
||||
}
|
||||
|
||||
// IsStarted returns true if the action has been started already.
|
||||
func (a Action) IsStarted() bool {
|
||||
return !a.StartTime.IsZero()
|
||||
|
|
|
@ -101,6 +101,7 @@ func removeMemberConditionsMapFunc(m *api.MemberStatus) {
|
|||
m.Conditions.Remove(api.ConditionTypeTopologyAware)
|
||||
m.Conditions.Remove(api.MemberReplacementRequired)
|
||||
m.Conditions.Remove(api.ConditionTypePVCResizePending)
|
||||
m.Conditions.Remove(api.ConditionTypeArchitectureMismatch)
|
||||
|
||||
m.RemoveTerminationsBefore(time.Now().Add(-1 * recentTerminationsKeepPeriod))
|
||||
|
||||
|
|
|
@ -60,6 +60,27 @@ func AppendArchSelector(a *core.NodeAffinity, arch api.ArangoDeploymentArchitect
|
|||
a.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = append(a.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms, arch.AsNodeSelectorRequirement())
|
||||
}
|
||||
|
||||
func GetArchFromAffinity(a *core.Affinity) api.ArangoDeploymentArchitectureType {
|
||||
if a != nil && a.NodeAffinity != nil && a.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
for _, nst := range a.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
|
||||
for _, req := range nst.MatchExpressions {
|
||||
if req.Key == shared.NodeArchAffinityLabel || req.Key == shared.NodeArchAffinityLabelBeta {
|
||||
for _, arch := range req.Values {
|
||||
return api.ArangoDeploymentArchitectureType(arch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SetArchInAffinity(a *core.Affinity, arch api.ArangoDeploymentArchitectureType) {
|
||||
if a != nil && a.NodeAffinity != nil && a.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
a.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = []core.NodeSelectorTerm{arch.AsNodeSelectorRequirement()}
|
||||
}
|
||||
}
|
||||
|
||||
func AppendAffinityWithRole(p interfaces.PodCreator, a *core.PodAffinity, role string) {
|
||||
labelSelector := &meta.LabelSelector{
|
||||
MatchLabels: k8sutil.LabelsForDeployment(p.GetName(), role),
|
||||
|
|
|
@ -195,6 +195,9 @@ var (
|
|||
_ Action = &actionSetCurrentImage{}
|
||||
_ actionFactory = newSetCurrentImageAction
|
||||
|
||||
_ Action = &actionSetCurrentMemberArch{}
|
||||
_ actionFactory = newSetCurrentMemberArchAction
|
||||
|
||||
_ Action = &actionSetMaintenanceCondition{}
|
||||
_ actionFactory = newSetMaintenanceConditionAction
|
||||
|
||||
|
@ -924,6 +927,18 @@ func init() {
|
|||
registerAction(action, function)
|
||||
}
|
||||
|
||||
// SetCurrentMemberArch
|
||||
{
|
||||
// Get Action defition
|
||||
function := newSetCurrentMemberArchAction
|
||||
action := api.ActionTypeSetCurrentMemberArch
|
||||
|
||||
// Wrap action main function
|
||||
|
||||
// Register action
|
||||
registerAction(action, function)
|
||||
}
|
||||
|
||||
// SetMaintenanceCondition
|
||||
{
|
||||
// Get Action defition
|
||||
|
|
|
@ -256,6 +256,10 @@ func Test_Actions(t *testing.T) {
|
|||
ActionsExistence(t, api.ActionTypeSetCurrentImage)
|
||||
})
|
||||
|
||||
t.Run("SetCurrentMemberArch", func(t *testing.T) {
|
||||
ActionsExistence(t, api.ActionTypeSetCurrentMemberArch)
|
||||
})
|
||||
|
||||
t.Run("SetMaintenanceCondition", func(t *testing.T) {
|
||||
ActionsExistence(t, api.ActionTypeSetMaintenanceCondition)
|
||||
})
|
||||
|
|
84
pkg/deployment/reconcile/action_set_current_member_arch.go
Normal file
84
pkg/deployment/reconcile/action_set_current_member_arch.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package reconcile
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/errors"
|
||||
)
|
||||
|
||||
// newSetCurrentMemberArchAction creates a new Action that implements the given planned SetMemberCurrentArch action.
|
||||
func newSetCurrentMemberArchAction(action api.Action, actionCtx ActionContext) Action {
|
||||
a := &actionSetCurrentMemberArch{}
|
||||
|
||||
a.actionImpl = newActionImplDefRef(action, actionCtx)
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// actionSetCurrentMemberArch implements an SetMemberCurrentArch.
|
||||
type actionSetCurrentMemberArch struct {
|
||||
// actionImpl implement timeout and member id functions
|
||||
actionImpl
|
||||
}
|
||||
|
||||
// Start performs the start of the action.
|
||||
// Returns true if the action is completely finished, false in case
|
||||
// the start time needs to be recorded and a ready condition needs to be checked.
|
||||
func (a *actionSetCurrentMemberArch) Start(ctx context.Context) (bool, error) {
|
||||
ready, _, err := a.CheckProgress(ctx)
|
||||
if err != nil {
|
||||
return false, errors.WithStack(err)
|
||||
}
|
||||
return ready, nil
|
||||
}
|
||||
|
||||
// CheckProgress checks the progress of the action.
|
||||
// Returns true if the action is completely finished, false otherwise.
|
||||
func (a *actionSetCurrentMemberArch) CheckProgress(ctx context.Context) (bool, bool, error) {
|
||||
arch := a.action.Architecture
|
||||
|
||||
if err := a.actionCtx.WithStatusUpdate(ctx, func(s *api.DeploymentStatus) bool {
|
||||
m, g, found := s.Members.ElementByID(a.action.MemberID)
|
||||
if !found {
|
||||
a.log.Error("No such member")
|
||||
return false
|
||||
}
|
||||
|
||||
if !m.Architecture.Equal(&arch) {
|
||||
m.Architecture = &arch
|
||||
}
|
||||
|
||||
if err := s.Members.Update(m, g); err != nil {
|
||||
a.log.Error("Member update failed")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}); err != nil {
|
||||
a.log.Error("Member failed")
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
return true, false, nil
|
||||
}
|
|
@ -251,3 +251,8 @@ func getRequiredReplaceMessage(podName string) string {
|
|||
return fmt.Sprintf("%s annotation is required to be set on the pod %s",
|
||||
deployment.ArangoDeploymentPodReplaceAnnotation, podName)
|
||||
}
|
||||
|
||||
func getRequiredRotateMessage(podName string) string {
|
||||
return fmt.Sprintf("%s annotation is required to be set on the pod %s",
|
||||
deployment.ArangoDeploymentPodRotateAnnotation, podName)
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ func (r *Reconciler) createHighPlan(ctx context.Context, apiObject k8sutil.APIOb
|
|||
ApplyIfEmpty(r.updateMemberRotationConditionsPlan).
|
||||
ApplyIfEmpty(r.createMemberRecreationConditionsPlan).
|
||||
ApplyIfEmpty(r.createRotateServerStoragePVCPendingResizeConditionPlan).
|
||||
ApplyIfEmpty(r.createChangeMemberArchPlan).
|
||||
ApplyIfEmpty(r.createRotateServerStorageResizePlanRuntime).
|
||||
ApplyIfEmpty(r.createTopologyMemberUpdatePlan).
|
||||
ApplyIfEmptyWithBackOff(LicenseCheck, 30*time.Second, r.updateClusterLicense).
|
||||
|
|
62
pkg/deployment/reconcile/plan_builder_rotate_arch.go
Normal file
62
pkg/deployment/reconcile/plan_builder_rotate_arch.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package reconcile
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/actions"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
|
||||
)
|
||||
|
||||
// createChangeMemberArchPlan goes over all pods to check if an Architecture type is set correctly
|
||||
func (r *Reconciler) createChangeMemberArchPlan(ctx context.Context,
|
||||
apiObject k8sutil.APIObject,
|
||||
spec api.DeploymentSpec, status api.DeploymentStatus,
|
||||
context PlanBuilderContext) api.Plan {
|
||||
var p api.Plan
|
||||
|
||||
for _, m := range status.Members.AsList() {
|
||||
member := m.Member
|
||||
cache, ok := context.ACS().ClusterCache(member.ClusterID)
|
||||
if !ok {
|
||||
return p
|
||||
}
|
||||
|
||||
if pod, ok := cache.Pod().V1().GetSimple(member.Pod.GetName()); ok {
|
||||
if v, ok := pod.GetAnnotations()[deployment.ArangoDeploymentPodChangeArchAnnotation]; ok {
|
||||
arch := api.ArangoDeploymentArchitectureType(v)
|
||||
if arch.IsArchMismatch(spec.Architecture, member.Architecture) {
|
||||
r.log.
|
||||
Str("pod-name", member.Pod.GetName()).
|
||||
Str("server-group", m.Group.AsRole()).
|
||||
Warn("try changing an Architecture type, but %s", getRequiredRotateMessage(member.Pod.GetName()))
|
||||
p = append(p,
|
||||
actions.NewAction(api.ActionTypeSetCurrentMemberArch, m.Group, member, "Architecture Mismatch").SetArch(arch),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
|
@ -29,6 +29,7 @@ import (
|
|||
core "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/deployment"
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
"github.com/arangodb/kube-arangodb/pkg/deployment/agency"
|
||||
|
@ -274,6 +275,15 @@ func (r *Resources) InspectPods(ctx context.Context, cachedStatus inspectorInter
|
|||
}
|
||||
}
|
||||
|
||||
// Member arch check
|
||||
if v, ok := pod.Annotations[deployment.ArangoDeploymentPodChangeArchAnnotation]; ok {
|
||||
if api.ArangoDeploymentArchitectureType(v).IsArchMismatch(spec.Architecture, memberStatus.Architecture) {
|
||||
if memberStatus.Conditions.Update(api.ConditionTypeArchitectureMismatch, true, "Member has a different architecture than the deployment", "") {
|
||||
updateMemberStatusNeeded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Member Maintenance
|
||||
if agencyCachePresent {
|
||||
if agencyCache.Current.MaintenanceServers.InMaintenance(agency.Server(memberStatus.ID)) {
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
package rotation
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
|
@ -55,7 +53,7 @@ func affinityCompare(_ api.DeploymentSpec, _ api.ServerGroup, spec, status *core
|
|||
e = err
|
||||
return
|
||||
} else if specC != statusC {
|
||||
mode = mode.And(getRotationMode(spec, status))
|
||||
mode = mode.And(SilentRotation)
|
||||
status.Affinity = spec.Affinity.DeepCopy()
|
||||
return
|
||||
} else {
|
||||
|
@ -64,21 +62,3 @@ func affinityCompare(_ api.DeploymentSpec, _ api.ServerGroup, spec, status *core
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getRotationMode(spec, status *core.PodSpec) Mode {
|
||||
var specArchs map[api.ArangoDeploymentArchitectureType]bool
|
||||
var statusArchs map[api.ArangoDeploymentArchitectureType]bool
|
||||
|
||||
if spec.Affinity != nil && spec.Affinity.NodeAffinity != nil && spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
specArchs = api.GetArchsFromNodeSelector(spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms)
|
||||
}
|
||||
|
||||
if status.Affinity != nil && status.Affinity.NodeAffinity != nil && status.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
statusArchs = api.GetArchsFromNodeSelector(status.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(specArchs, statusArchs) {
|
||||
return GracefulRotation
|
||||
}
|
||||
return SilentRotation
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ func Test_ArangoD_Affinity(t *testing.T) {
|
|||
}),
|
||||
|
||||
TestCaseOverride: TestCaseOverride{
|
||||
expectedMode: GracefulRotation,
|
||||
expectedMode: SilentRotation,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -180,7 +180,7 @@ func Test_ArangoD_Affinity(t *testing.T) {
|
|||
}),
|
||||
|
||||
TestCaseOverride: TestCaseOverride{
|
||||
expectedMode: GracefulRotation,
|
||||
expectedMode: SilentRotation,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -278,7 +278,7 @@ func Test_ArangoD_Affinity(t *testing.T) {
|
|||
}),
|
||||
|
||||
TestCaseOverride: TestCaseOverride{
|
||||
expectedMode: GracefulRotation,
|
||||
expectedMode: SilentRotation,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -36,6 +36,7 @@ type Mode int
|
|||
|
||||
const (
|
||||
SkippedRotation Mode = iota
|
||||
// SilentRotation Propagates changes without restart
|
||||
SilentRotation
|
||||
InPlaceRotation
|
||||
GracefulRotation
|
||||
|
|
Loading…
Reference in a new issue