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

[Feature] Add UpToDate Condition (#540)

This commit is contained in:
Adam Janikowski 2020-04-01 15:38:03 +02:00 committed by GitHub
parent fd8e98aee9
commit 99770ce2bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 655 additions and 575 deletions

View file

@ -1,6 +1,7 @@
# Change Log # Change Log
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
- Added UpToDate condition in ArangoDeployment Status
## [1.0.1](https://github.com/arangodb/kube-arangodb/tree/1.0.1) (2020-03-25) ## [1.0.1](https://github.com/arangodb/kube-arangodb/tree/1.0.1) (2020-03-25)
- Added Customizable Affinity settings for ArangoDB Member Pods - Added Customizable Affinity settings for ArangoDB Member Pods

View file

@ -59,6 +59,8 @@ const (
ConditionTypeBootstrapSucceded ConditionType = "BootstrapSucceded" ConditionTypeBootstrapSucceded ConditionType = "BootstrapSucceded"
// ConditionTypeTerminating indicates that the member is terminating but not yet terminated. // ConditionTypeTerminating indicates that the member is terminating but not yet terminated.
ConditionTypeTerminating ConditionType = "Terminating" ConditionTypeTerminating ConditionType = "Terminating"
// ConditionTypeTerminating indicates that the deployment is up to date.
ConditionTypeUpToDate ConditionType = "UpToDate"
) )
// Condition represents one current condition of a deployment or deployment member. // Condition represents one current condition of a deployment or deployment member.
@ -79,6 +81,10 @@ type Condition struct {
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
} }
func (c Condition) IsTrue() bool {
return c.Status == v1.ConditionTrue
}
// ConditionList is a list of conditions. // ConditionList is a list of conditions.
// Each type is allowed only once. // Each type is allowed only once.
type ConditionList []Condition type ConditionList []Condition
@ -116,12 +122,17 @@ func (c Condition) Equal(other Condition) bool {
// IsTrue return true when a condition with given type exists and its status is `True`. // IsTrue return true when a condition with given type exists and its status is `True`.
func (list ConditionList) IsTrue(conditionType ConditionType) bool { func (list ConditionList) IsTrue(conditionType ConditionType) bool {
c, found := list.Get(conditionType) c, found := list.Get(conditionType)
return found && c.Status == v1.ConditionTrue return found && c.IsTrue()
} }
// Get a condition by type. // Get a condition by type.
// Returns true if found, false if not found. // Returns true if found, false if not found.
func (list ConditionList) Get(conditionType ConditionType) (Condition, bool) { func (list ConditionList) Get(conditionType ConditionType) (Condition, bool) {
// Covers nil and empty lists
if len(list) == 0 {
return Condition{}, false
}
for _, x := range list { for _, x := range list {
if x.Type == conditionType { if x.Type == conditionType {
return x, true return x, true

View file

@ -93,3 +93,13 @@ func (d *ArangoDeployment) ForeachServerGroup(cb ServerGroupFunc, status *Deploy
} }
return nil return nil
} }
// IsUpToDate checks if applied version match current version in spec
func (d ArangoDeployment) IsUpToDate() (bool, error) {
sha, err := d.Spec.Checksum()
if err != nil {
return false, err
}
return sha == d.Status.AppliedVersion && d.Status.Conditions.IsTrue(ConditionTypeUpToDate), nil
}

View file

@ -23,6 +23,9 @@
package v1 package v1
import ( import (
"crypto/sha256"
"encoding/json"
"fmt"
"reflect" "reflect"
"github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util"
@ -413,3 +416,13 @@ func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string {
} }
return resetFields return resetFields
} }
// Checksum return checksum of current ArangoDeployment Spec section
func (s DeploymentSpec) Checksum() (string, error) {
data, err := json.Marshal(s)
if err != nil {
return "", err
}
return fmt.Sprintf("%0x", sha256.Sum256(data)), nil
}

View file

@ -33,6 +33,9 @@ type DeploymentStatus struct {
// Reason contains a human readable reason for reaching the current state (can be empty) // Reason contains a human readable reason for reaching the current state (can be empty)
Reason string `json:"reason,omitempty"` // Reason for current state Reason string `json:"reason,omitempty"` // Reason for current state
// AppliedVersion defines checksum of applied spec
AppliedVersion string `json:"appliedVersion"`
// ServiceName holds the name of the Service a client can use (inside the k8s cluster) // ServiceName holds the name of the Service a client can use (inside the k8s cluster)
// to access ArangoDB. // to access ArangoDB.
ServiceName string `json:"serviceName,omitempty"` ServiceName string `json:"serviceName,omitempty"`

View file

@ -25,6 +25,7 @@ package v1
import ( import (
"github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util"
"github.com/dchest/uniuri" "github.com/dchest/uniuri"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@ -66,6 +67,8 @@ const (
ActionTypePVCResize ActionType = "PVCResize" ActionTypePVCResize ActionType = "PVCResize"
// ActionTypePVCResized waits for PVC to resize for defined time // ActionTypePVCResized waits for PVC to resize for defined time
ActionTypePVCResized ActionType = "PVCResized" ActionTypePVCResized ActionType = "PVCResized"
// UpToDateUpdateResized define up to date annotation in spec
UpToDateUpdate ActionType = "UpToDateUpdate"
) )
const ( const (
@ -92,6 +95,8 @@ type Action struct {
Reason string `json:"reason,omitempty"` Reason string `json:"reason,omitempty"`
// Image used in can of a SetCurrentImage action. // Image used in can of a SetCurrentImage action.
Image string `json:"image,omitempty"` Image string `json:"image,omitempty"`
// Params additional parameters used for action
Params map[string]interface{} `json:"params,omitempty"`
} }
// Equal compares two Actions // Equal compares two Actions
@ -103,7 +108,30 @@ func (a Action) Equal(other Action) bool {
util.TimeCompareEqual(a.CreationTime, other.CreationTime) && util.TimeCompareEqual(a.CreationTime, other.CreationTime) &&
util.TimeCompareEqualPointer(a.StartTime, other.StartTime) && util.TimeCompareEqualPointer(a.StartTime, other.StartTime) &&
a.Reason == other.Reason && a.Reason == other.Reason &&
a.Image == other.Image a.Image == other.Image &&
equality.Semantic.DeepEqual(a.Params, other.Params)
}
// AddParam returns copy of action with set parameter
func (a Action) AddParam(key string, value interface{}) Action {
if a.Params == nil {
a.Params = map[string]interface{}{}
}
a.Params[key] = value
return a
}
// GetParam returns action parameter
func (a Action) GetParam(key string) (interface{}, bool) {
if a.Params == nil {
return nil, false
}
i, ok := a.Params[key]
return i, ok
} }
// NewAction instantiates a new Action. // NewAction instantiates a new Action.

View file

@ -103,6 +103,10 @@ func (d *Deployment) GetStatus() (api.DeploymentStatus, int32) {
d.status.mutex.Lock() d.status.mutex.Lock()
defer d.status.mutex.Unlock() defer d.status.mutex.Unlock()
return d.getStatus()
}
func (d *Deployment) getStatus() (api.DeploymentStatus, int32) {
version := d.status.version version := d.status.version
return *d.status.last.DeepCopy(), version return *d.status.last.DeepCopy(), version
} }
@ -115,6 +119,10 @@ func (d *Deployment) UpdateStatus(status api.DeploymentStatus, lastVersion int32
d.status.mutex.Lock() d.status.mutex.Lock()
defer d.status.mutex.Unlock() defer d.status.mutex.Unlock()
return d.updateStatus(status, lastVersion, force...)
}
func (d *Deployment) updateStatus(status api.DeploymentStatus, lastVersion int32, force ...bool) error {
if d.status.version != lastVersion { if d.status.version != lastVersion {
// Status is obsolete // Status is obsolete
d.deps.Log.Error(). d.deps.Log.Error().
@ -483,3 +491,18 @@ func (d *Deployment) GetMetricsExporterImage() string {
func (d *Deployment) GetArangoImage() string { func (d *Deployment) GetArangoImage() string {
return d.config.ArangoImage return d.config.ArangoImage
} }
func (d *Deployment) WithStatusUpdate(action func(s *api.DeploymentStatus) bool, force ...bool) error {
d.status.mutex.Lock()
defer d.status.mutex.Unlock()
status, version := d.getStatus()
changed := action(&status)
if !changed {
return nil
}
return d.updateStatus(status, version, force...)
}

View file

@ -26,6 +26,8 @@ import (
"context" "context"
"time" "time"
"github.com/pkg/errors"
"github.com/arangodb/kube-arangodb/pkg/apis/deployment" "github.com/arangodb/kube-arangodb/pkg/apis/deployment"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -84,138 +86,11 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
return nextInterval return nextInterval
} }
// Inspect secret hashes if inspectNextInterval, err := d.inspectDeploymentWithError(ctx, nextInterval); err != nil {
if err := d.resources.ValidateSecretHashes(); err != nil { nextInterval = inspectNextInterval
hasError = true hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Secret hash validation failed", err, d.apiObject))
}
// Check for LicenseKeySecret d.CreateEvent(k8sutil.NewErrorEvent("Reconcilation failed", err, d.apiObject))
if err := d.resources.ValidateLicenseKeySecret(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("License Key Secret invalid", err, d.apiObject))
}
// Is the deployment in a good state?
status, _ := d.GetStatus()
if status.Conditions.IsTrue(api.ConditionTypeSecretsChanged) {
log.Debug().Msg("Condition SecretsChanged is true. Revert secrets before we can continue")
return nextInterval
}
// Ensure we have image info
if retrySoon, err := d.ensureImages(d.apiObject); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Image detection failed", err, d.apiObject))
} else if retrySoon {
nextInterval = minInspectionInterval
}
// Inspection of generated resources needed
if x, err := d.resources.InspectPods(ctx); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Pod inspection failed", err, d.apiObject))
} else {
nextInterval = nextInterval.ReduceTo(x)
}
if x, err := d.resources.InspectPVCs(ctx); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("PVC inspection failed", err, d.apiObject))
} else {
nextInterval = nextInterval.ReduceTo(x)
}
// Check members for resilience
if err := d.resilience.CheckMemberFailure(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Member failure detection failed", err, d.apiObject))
}
// Immediate actions
if err := d.reconciler.CheckDeployment(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Reconciler immediate actions failed", err, d.apiObject))
}
// Create scale/update plan
if err := d.reconciler.CreatePlan(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Plan creation failed", err, d.apiObject))
}
// Execute current step of scale/update plan
retrySoon, err := d.reconciler.ExecutePlan(ctx)
if err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Plan execution failed", err, d.apiObject))
}
if retrySoon {
nextInterval = minInspectionInterval
}
// Ensure all resources are created
if err := d.resources.EnsureSecrets(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Secret creation failed", err, d.apiObject))
}
if err := d.resources.EnsureServices(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Service creation failed", err, d.apiObject))
}
if d.haveServiceMonitorCRD {
if err := d.resources.EnsureServiceMonitor(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Service monitor creation failed", err, d.apiObject))
}
}
if err := d.resources.EnsurePVCs(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("PVC creation failed", err, d.apiObject))
}
if err := d.resources.EnsurePods(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Pod creation failed", err, d.apiObject))
}
if err := d.resources.EnsurePDBs(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("PDB creation failed", err, d.apiObject))
}
if err := d.resources.EnsureAnnotations(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Annotation update failed", err, d.apiObject))
}
// Create access packages
if err := d.createAccessPackages(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("AccessPackage creation failed", err, d.apiObject))
}
// Ensure deployment bootstrap
if err := d.EnsureBootstrap(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Bootstrap failed", err, d.apiObject))
}
// Inspect deployment for obsolete members
if err := d.resources.CleanupRemovedMembers(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Removed member cleanup failed", err, d.apiObject))
}
if err := d.backup.CheckRestore(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Restore operation failed", err, d.apiObject))
}
// At the end of the inspect, we cleanup terminated pods.
if x, err := d.resources.CleanupTerminatedPods(); err != nil {
hasError = true
d.CreateEvent(k8sutil.NewErrorEvent("Pod cleanup failed", err, d.apiObject))
} else {
nextInterval = nextInterval.ReduceTo(x)
} }
} }
@ -231,6 +106,170 @@ func (d *Deployment) inspectDeployment(lastInterval util.Interval) util.Interval
return nextInterval.ReduceTo(maxInspectionInterval) return nextInterval.ReduceTo(maxInspectionInterval)
} }
func (d *Deployment) inspectDeploymentWithError(ctx context.Context, lastInterval util.Interval) (nextInterval util.Interval, inspectError error) {
// Ensure that spec and status checksum are same
spec := d.GetSpec()
status, _ := d.getStatus()
nextInterval = lastInterval
inspectError = nil
checksum, err := spec.Checksum()
if err != nil {
return minInspectionInterval, errors.Wrapf(err, "Calculation of spec failed")
} else {
condition, exists := status.Conditions.Get(api.ConditionTypeUpToDate)
if (checksum != status.AppliedVersion && (!exists || condition.IsTrue())) ||
(checksum == status.AppliedVersion && (!exists || !condition.IsTrue())) {
if err = d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
if checksum == status.AppliedVersion {
return s.Conditions.Update(api.ConditionTypeUpToDate, true, "Everything is UpToDate", "Spec applied")
}
return s.Conditions.Update(api.ConditionTypeUpToDate, false, "Spec Changed", "Spec Object changed. Waiting until plan will be applied")
}); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
}
return minInspectionInterval, nil // Retry ASAP
}
}
// Inspect secret hashes
if err := d.resources.ValidateSecretHashes(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Secret hash validation failed")
}
// Check for LicenseKeySecret
if err := d.resources.ValidateLicenseKeySecret(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "License Key Secret invalid")
}
// Is the deployment in a good state?
if status.Conditions.IsTrue(api.ConditionTypeSecretsChanged) {
return minInspectionInterval, errors.Errorf("Secrets changed")
}
// Ensure we have image info
if retrySoon, err := d.ensureImages(d.apiObject); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Image detection failed")
} else if retrySoon {
return minInspectionInterval, nil
}
// Inspection of generated resources needed
if x, err := d.resources.InspectPods(ctx); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Pod inspection failed")
} else {
nextInterval = nextInterval.ReduceTo(x)
}
if x, err := d.resources.InspectPVCs(ctx); err != nil {
return minInspectionInterval, errors.Wrapf(err, "PVC inspection failed")
} else {
nextInterval = nextInterval.ReduceTo(x)
}
// Check members for resilience
if err := d.resilience.CheckMemberFailure(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Member failure detection failed")
}
// Immediate actions
if err := d.reconciler.CheckDeployment(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Reconciler immediate actions failed")
}
if interval, err := d.ensureResources(nextInterval); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Reconciler resource recreation failed")
} else {
nextInterval = interval
}
// Create scale/update plan
if err := d.reconciler.CreatePlan(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Plan creation failed")
}
// Execute current step of scale/update plan
retrySoon, err := d.reconciler.ExecutePlan(ctx)
if err != nil {
return minInspectionInterval, errors.Wrapf(err, "Plan execution failed")
}
if retrySoon {
nextInterval = minInspectionInterval
} else {
// Do not retry - so plan is empty
if status.AppliedVersion != checksum {
if err := d.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
s.AppliedVersion = checksum
return true
}); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Unable to update UpToDate condition")
}
return minInspectionInterval, nil
}
}
// Create access packages
if err := d.createAccessPackages(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "AccessPackage creation failed")
}
// Ensure deployment bootstrap
if err := d.EnsureBootstrap(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Bootstrap failed")
}
// Inspect deployment for obsolete members
if err := d.resources.CleanupRemovedMembers(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Removed member cleanup failed")
}
if err := d.backup.CheckRestore(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Restore operation failed")
}
// At the end of the inspect, we cleanup terminated pods.
if x, err := d.resources.CleanupTerminatedPods(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Pod cleanup failed")
} else {
nextInterval = nextInterval.ReduceTo(x)
}
return
}
func (d *Deployment) ensureResources(lastInterval util.Interval) (util.Interval, error) {
// Ensure all resources are created
if err := d.resources.EnsureSecrets(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Secret creation failed")
}
if err := d.resources.EnsureServices(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Service creation failed")
}
if d.haveServiceMonitorCRD {
if err := d.resources.EnsureServiceMonitor(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Service monitor creation failed")
}
}
if err := d.resources.EnsurePVCs(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "PVC creation failed")
}
if err := d.resources.EnsurePods(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Pod creation failed")
}
if err := d.resources.EnsurePDBs(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "PDB creation failed")
}
if err := d.resources.EnsureAnnotations(); err != nil {
return minInspectionInterval, errors.Wrapf(err, "Annotation update failed")
}
return lastInterval, nil
}
// triggerInspection ensures that an inspection is run soon. // triggerInspection ensures that an inspection is run soon.
func (d *Deployment) triggerInspection() { func (d *Deployment) triggerInspection() {
d.inspectTrigger.Trigger() d.inspectTrigger.Trigger()

View file

@ -24,7 +24,12 @@ package reconcile
import ( import (
"context" "context"
"fmt"
"sync"
"time" "time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog"
) )
// Action executes a single Plan item. // Action executes a single Plan item.
@ -41,3 +46,30 @@ type Action interface {
// Return the MemberID used / created in this action // Return the MemberID used / created in this action
MemberID() string MemberID() string
} }
type actionFactory func(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action
var (
actions = map[api.ActionType]actionFactory{}
actionsLock sync.Mutex
)
func registerAction(t api.ActionType, f actionFactory) {
actionsLock.Lock()
defer actionsLock.Unlock()
_, ok := actions[t]
if ok {
panic(fmt.Sprintf("Action already defined %s", t))
}
actions[t] = f
}
func getActionFactory(t api.ActionType) (actionFactory, bool) {
actionsLock.Lock()
defer actionsLock.Unlock()
f, ok := actions[t]
return f, ok
}

View file

@ -24,28 +24,34 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
// NewAddMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeAddMember, newAddMemberAction)
}
// newAddMemberAction creates a new Action that implements the given
// planned AddMember action. // planned AddMember action.
func NewAddMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newAddMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionAddMember{ a := &actionAddMember{}
log: log,
action: action, a.actionImpl = newActionImpl(log, action, actionCtx, addMemberTimeout, &a.newMemberID)
actionCtx: actionCtx,
} return a
} }
// actionAddMember implements an AddMemberAction. // actionAddMember implements an AddMemberAction.
type actionAddMember struct { type actionAddMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
newMemberID string newMemberID string
} }
@ -61,20 +67,3 @@ func (a *actionAddMember) Start(ctx context.Context) (bool, error) {
a.newMemberID = newID a.newMemberID = newID
return true, nil return true, nil
} }
// CheckProgress checks the progress of the action.
// Returns true if the action is completely finished, false otherwise.
func (a *actionAddMember) CheckProgress(ctx context.Context) (bool, bool, error) {
// Nothing todo
return true, false, nil
}
// Timeout returns the amount of time after which this action will timeout.
func (a *actionAddMember) Timeout() time.Duration {
return addMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionAddMember) MemberID() string {
return a.newMemberID
}

View file

@ -24,7 +24,6 @@ package reconcile
import ( import (
"context" "context"
"time"
driver "github.com/arangodb/go-driver" driver "github.com/arangodb/go-driver"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
@ -33,21 +32,24 @@ import (
"github.com/arangodb/kube-arangodb/pkg/util/arangod" "github.com/arangodb/kube-arangodb/pkg/util/arangod"
) )
// NewCleanOutMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeCleanOutMember, newCleanOutMemberAction)
}
// newCleanOutMemberAction creates a new Action that implements the given
// planned CleanOutMember action. // planned CleanOutMember action.
func NewCleanOutMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newCleanOutMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionCleanoutMember{ a := &actionCleanoutMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, cleanoutMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionCleanoutMember implements an CleanOutMemberAction. // actionCleanoutMember implements an CleanOutMemberAction.
type actionCleanoutMember struct { type actionCleanoutMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -154,13 +156,3 @@ func (a *actionCleanoutMember) CheckProgress(ctx context.Context) (bool, bool, e
// Cleanout completed // Cleanout completed
return true, false, nil return true, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionCleanoutMember) Timeout() time.Duration {
return cleanoutMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionCleanoutMember) MemberID() string {
return a.action.MemberID
}

View file

@ -109,6 +109,8 @@ type ActionContext interface {
DisableScalingCluster() error DisableScalingCluster() error
// EnableScalingCluster enables scaling DBservers and coordinators // EnableScalingCluster enables scaling DBservers and coordinators
EnableScalingCluster() error EnableScalingCluster() error
// WithStatusUpdate update status of ArangoDeployment with defined modifier. If action returns True action is taken
UpdateClusterCondition(conditionType api.ConditionType, status bool, reason, message string) error
} }
// newActionContext creates a new ActionContext implementation. // newActionContext creates a new ActionContext implementation.
@ -125,6 +127,12 @@ type actionContext struct {
context Context context Context
} }
func (ac *actionContext) UpdateClusterCondition(conditionType api.ConditionType, status bool, reason, message string) error {
return ac.context.WithStatusUpdate(func(s *api.DeploymentStatus) bool {
return s.Conditions.Update(conditionType, status, reason, message)
})
}
func (ac *actionContext) GetPv(pvName string) (*v1.PersistentVolume, error) { func (ac *actionContext) GetPv(pvName string) (*v1.PersistentVolume, error) {
return ac.context.GetPv(pvName) return ac.context.GetPv(pvName)
} }

View file

@ -22,27 +22,32 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// actionDisableScalingCluster implements disabling scaling DBservers and coordinators. func init() {
type actionDisableScalingCluster struct { registerAction(api.ActionTypeDisableClusterScaling, newDisableScalingCluster)
log zerolog.Logger
action api.Action
actionCtx ActionContext
newMemberID string
} }
// NewDisableScalingCluster creates the new action with disabling scaling DBservers and coordinators. // newDisableScalingCluster creates the new action with disabling scaling DBservers and coordinators.
func NewDisableScalingCluster(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newDisableScalingCluster(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionDisableScalingCluster{ a := &actionDisableScalingCluster{}
log: log,
action: action, a.actionImpl = newActionImpl(log, action, actionCtx, 0, util.NewString(""))
actionCtx: actionCtx,
} return a
}
// actionDisableScalingCluster implements disabling scaling DBservers and coordinators.
type actionDisableScalingCluster struct {
// actionImpl implement timeout and member id functions
actionImpl
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start disables scaling DBservers and coordinators // Start disables scaling DBservers and coordinators
@ -53,18 +58,3 @@ func (a *actionDisableScalingCluster) Start(ctx context.Context) (bool, error) {
} }
return true, nil return true, nil
} }
// CheckProgress does not matter. Everything is done in 'Start' function
func (a *actionDisableScalingCluster) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil
}
// Timeout does not matter. Everything is done in 'Start' function
func (a *actionDisableScalingCluster) Timeout() time.Duration {
return 0
}
// MemberID is not used
func (a *actionDisableScalingCluster) MemberID() string {
return ""
}

View file

@ -22,27 +22,32 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// actionEnableScalingCluster implements enabling scaling DBservers and coordinators. func init() {
type actionEnableScalingCluster struct { registerAction(api.ActionTypeEnableClusterScaling, newEnableScalingCluster)
log zerolog.Logger
action api.Action
actionCtx ActionContext
newMemberID string
} }
// NewEnableScalingCluster creates the new action with enabling scaling DBservers and coordinators. // newEnableScalingCluster creates the new action with enabling scaling DBservers and coordinators.
func NewEnableScalingCluster(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newEnableScalingCluster(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionEnableScalingCluster{ a := &actionEnableScalingCluster{}
log: log,
action: action, a.actionImpl = newActionImpl(log, action, actionCtx, 0, util.NewString(""))
actionCtx: actionCtx,
} return a
}
// actionEnableScalingCluster implements enabling scaling DBservers and coordinators.
type actionEnableScalingCluster struct {
// actionImpl implement timeout and member id functions
actionImpl
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start enables scaling DBservers and coordinators // Start enables scaling DBservers and coordinators
@ -53,18 +58,3 @@ func (a *actionEnableScalingCluster) Start(ctx context.Context) (bool, error) {
} }
return true, nil return true, nil
} }
// CheckProgress does not matter. Everything is done in 'Start' function
func (a *actionEnableScalingCluster) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil
}
// Timeout does not matter. Everything is done in 'Start' function
func (a *actionEnableScalingCluster) Timeout() time.Duration {
return 0
}
// MemberID is not used
func (a *actionEnableScalingCluster) MemberID() string {
return ""
}

View file

@ -0,0 +1,84 @@
//
// DISCLAIMER
//
// Copyright 2020 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
//
// Author Adam Janikowski
//
package reconcile
import (
"context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog"
)
type actionEmptyCheckProgress struct {
}
// CheckProgress define optional check progress for action
// Returns: ready, abort, error.
func (e actionEmptyCheckProgress) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil
}
type actionEmptyStart struct {
}
func (e actionEmptyStart) Start(ctx context.Context) (bool, error) {
return false, nil
}
func newActionImplDefRef(log zerolog.Logger, action api.Action, actionCtx ActionContext, timeout time.Duration) actionImpl {
return newActionImpl(log, action, actionCtx, timeout, &action.MemberID)
}
func newActionImpl(log zerolog.Logger, action api.Action, actionCtx ActionContext, timeout time.Duration, memberIDRef *string) actionImpl {
if memberIDRef == nil {
panic("Action cannot have nil reference to member!")
}
return actionImpl{
log: log,
action: action,
actionCtx: actionCtx,
timeout: timeout,
memberIDRef: memberIDRef,
}
}
type actionImpl struct {
log zerolog.Logger
action api.Action
actionCtx ActionContext
timeout time.Duration
memberIDRef *string
}
// Timeout returns the amount of time after which this action will timeout.
func (a actionImpl) Timeout() time.Duration {
return a.timeout
}
// Return the MemberID used / created in this action
func (a actionImpl) MemberID() string {
return *a.memberIDRef
}

View file

@ -24,7 +24,6 @@ package reconcile
import ( import (
"context" "context"
"time"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
@ -34,21 +33,24 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRotateMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypePVCResize, newPVCResizeAction)
}
// newRotateMemberAction creates a new Action that implements the given
// planned RotateMember action. // planned RotateMember action.
func NewPVCResizeAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newPVCResizeAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionPVCResize{ a := &actionPVCResize{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, pvcResizeTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRotateMember implements an RotateMember. // actionRotateMember implements an RotateMember.
type actionPVCResize struct { type actionPVCResize struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -147,13 +149,3 @@ func (a *actionPVCResize) CheckProgress(ctx context.Context) (bool, bool, error)
return false, false, nil return false, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionPVCResize) Timeout() time.Duration {
return pvcResizeTimeout
}
// Return the MemberID used / created in this action
func (a *actionPVCResize) MemberID() string {
return a.action.MemberID
}

View file

@ -24,7 +24,6 @@ package reconcile
import ( import (
"context" "context"
"time"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
@ -34,28 +33,27 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRotateMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypePVCResized, newPVCResizedAction)
}
// newRotateMemberAction creates a new Action that implements the given
// planned RotateMember action. // planned RotateMember action.
func NewPVCResizedAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newPVCResizedAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionPVCResized{ a := &actionPVCResized{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, pvcResizedTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRotateMember implements an RotateMember. // actionRotateMember implements an RotateMember.
type actionPVCResized struct { type actionPVCResized struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
}
// Start performs the start of the action. // actionEmptyStart empty start function
// Returns true if the action is completely finished, false in case actionEmptyStart
// the start time needs to be recorded and a ready condition needs to be checked.
func (a *actionPVCResized) Start(ctx context.Context) (bool, error) {
return false, nil
} }
// CheckProgress checks the progress of the action. // CheckProgress checks the progress of the action.
@ -94,13 +92,3 @@ func (a *actionPVCResized) CheckProgress(ctx context.Context) (bool, bool, error
return false, false, nil return false, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionPVCResized) Timeout() time.Duration {
return pvcResizedTimeout
}
// Return the MemberID used / created in this action
func (a *actionPVCResized) MemberID() string {
return a.action.MemberID
}

View file

@ -25,7 +25,6 @@ package reconcile
import ( import (
"context" "context"
"fmt" "fmt"
"time"
kubeErrors "k8s.io/apimachinery/pkg/api/errors" kubeErrors "k8s.io/apimachinery/pkg/api/errors"
@ -34,21 +33,27 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
) )
// NewRecreateMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRecreateMember, newRecreateMemberAction)
}
// newRecreateMemberAction creates a new Action that implements the given
// planned RecreateMember action. // planned RecreateMember action.
func NewRecreateMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRecreateMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionRecreateMember{ a := &actionRecreateMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, recreateMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRecreateMember implements an RecreateMemberAction. // actionRecreateMember implements an RecreateMemberAction.
type actionRecreateMember struct { type actionRecreateMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -80,20 +85,3 @@ func (a *actionRecreateMember) Start(ctx context.Context) (bool, error) {
return true, nil return true, nil
} }
// CheckProgress checks the progress of the action.
// Returns true if the action is completely finished, false otherwise.
func (a *actionRecreateMember) CheckProgress(ctx context.Context) (bool, bool, error) {
// Nothing todo
return true, false, nil
}
// Timeout returns the amount of time after which this action will timeout.
func (a *actionRecreateMember) Timeout() time.Duration {
return recreateMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionRecreateMember) MemberID() string {
return a.action.MemberID
}

View file

@ -25,7 +25,6 @@ package reconcile
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@ -35,21 +34,27 @@ import (
"github.com/arangodb/kube-arangodb/pkg/util/arangod" "github.com/arangodb/kube-arangodb/pkg/util/arangod"
) )
// NewRemoveMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRemoveMember, newRemoveMemberAction)
}
// newRemoveMemberAction creates a new Action that implements the given
// planned RemoveMember action. // planned RemoveMember action.
func NewRemoveMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRemoveMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionRemoveMember{ a := &actionRemoveMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, removeMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRemoveMember implements an RemoveMemberAction. // actionRemoveMember implements an RemoveMemberAction.
type actionRemoveMember struct { type actionRemoveMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -114,20 +119,3 @@ func (a *actionRemoveMember) Start(ctx context.Context) (bool, error) {
} }
return true, nil return true, nil
} }
// CheckProgress checks the progress of the action.
// Returns true if the action is completely finished, false otherwise.
func (a *actionRemoveMember) CheckProgress(ctx context.Context) (bool, bool, error) {
// Nothing todo
return true, false, nil
}
// Timeout returns the amount of time after which this action will timeout.
func (a *actionRemoveMember) Timeout() time.Duration {
return removeMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionRemoveMember) MemberID() string {
return a.action.MemberID
}

View file

@ -30,21 +30,27 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRenewTLSCACertificateAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRenewTLSCACertificate, newRenewTLSCACertificateAction)
}
// newRenewTLSCACertificateAction creates a new Action that implements the given
// planned RenewTLSCACertificate action. // planned RenewTLSCACertificate action.
func NewRenewTLSCACertificateAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRenewTLSCACertificateAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &renewTLSCACertificateAction{ a := &renewTLSCACertificateAction{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, renewTLSCACertificateTimeout)
actionCtx: actionCtx,
} return a
} }
// renewTLSCACertificateAction implements a RenewTLSCACertificate action. // renewTLSCACertificateAction implements a RenewTLSCACertificate action.
type renewTLSCACertificateAction struct { type renewTLSCACertificateAction struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start performs the start of the action. // Start performs the start of the action.

View file

@ -24,27 +24,32 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRenewTLSCertificateAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRenewTLSCertificate, newRenewTLSCertificateAction)
}
// newRenewTLSCertificateAction creates a new Action that implements the given
// planned RenewTLSCertificate action. // planned RenewTLSCertificate action.
func NewRenewTLSCertificateAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRenewTLSCertificateAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &renewTLSCertificateAction{ a := &renewTLSCertificateAction{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, renewTLSCertificateTimeout)
actionCtx: actionCtx,
} return a
} }
// renewTLSCertificateAction implements a RenewTLSCertificate action. // renewTLSCertificateAction implements a RenewTLSCertificate action.
type renewTLSCertificateAction struct { type renewTLSCertificateAction struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -64,19 +69,3 @@ func (a *renewTLSCertificateAction) Start(ctx context.Context) (bool, error) {
} }
return false, nil return false, nil
} }
// CheckProgress checks the progress of the action.
// Returns true if the action is completely finished, false otherwise.
func (a *renewTLSCertificateAction) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil
}
// Timeout returns the amount of time after which this action will timeout.
func (a *renewTLSCertificateAction) Timeout() time.Duration {
return renewTLSCertificateTimeout
}
// Return the MemberID used / created in this action
func (a *renewTLSCertificateAction) MemberID() string {
return a.action.MemberID
}

View file

@ -24,27 +24,29 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRotateMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRotateMember, newRotateMemberAction)
}
// newRotateMemberAction creates a new Action that implements the given
// planned RotateMember action. // planned RotateMember action.
func NewRotateMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRotateMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionRotateMember{ a := &actionRotateMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, rotateMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRotateMember implements an RotateMember. // actionRotateMember implements an RotateMember.
type actionRotateMember struct { type actionRotateMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -123,13 +125,3 @@ func (a *actionRotateMember) CheckProgress(ctx context.Context) (bool, bool, err
} }
return true, false, nil return true, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionRotateMember) Timeout() time.Duration {
return rotateMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionRotateMember) MemberID() string {
return a.action.MemberID
}

View file

@ -24,27 +24,29 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRotateStartMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRotateStartMember, newRotateStartMemberAction)
}
// newRotateStartMemberAction creates a new Action that implements the given
// planned RotateStartMember action. // planned RotateStartMember action.
func NewRotateStartMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRotateStartMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionRotateStartMember{ a := &actionRotateStartMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, rotateMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRotateStartMember implements an RotateStartMember. // actionRotateStartMember implements an RotateStartMember.
type actionRotateStartMember struct { type actionRotateStartMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -116,13 +118,3 @@ func (a *actionRotateStartMember) CheckProgress(ctx context.Context) (bool, bool
} }
return true, false, nil return true, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionRotateStartMember) Timeout() time.Duration {
return rotateMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionRotateStartMember) MemberID() string {
return a.action.MemberID
}

View file

@ -24,27 +24,32 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewRotateStopMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeRotateStopMember, newRotateStopMemberAction)
}
// newRotateStopMemberAction creates a new Action that implements the given
// planned RotateStopMember action. // planned RotateStopMember action.
func NewRotateStopMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newRotateStopMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionRotateStopMember{ a := &actionRotateStopMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, rotateMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionRotateStopMember implements an RotateStopMember. // actionRotateStopMember implements an RotateStopMember.
type actionRotateStopMember struct { type actionRotateStopMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
// actionEmptyCheckProgress implement check progress with empty implementation
actionEmptyCheckProgress
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -65,19 +70,3 @@ func (a *actionRotateStopMember) Start(ctx context.Context) (bool, error) {
} }
return false, nil return false, nil
} }
// CheckProgress checks the progress of the action.
// Returns: ready, abort, error.
func (a *actionRotateStopMember) CheckProgress(ctx context.Context) (bool, bool, error) {
return true, false, nil
}
// Timeout returns the amount of time after which this action will timeout.
func (a *actionRotateStopMember) Timeout() time.Duration {
return rotateMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionRotateStopMember) MemberID() string {
return a.action.MemberID
}

View file

@ -24,31 +24,29 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
const ( func init() {
shutdownTimeout = time.Second * 15 registerAction(api.ActionTypeShutdownMember, newShutdownMemberAction)
) }
// NewShutdownMemberAction creates a new Action that implements the given // newShutdownMemberAction creates a new Action that implements the given
// planned ShutdownMember action. // planned ShutdownMember action.
func NewShutdownMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newShutdownMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionShutdownMember{ a := &actionShutdownMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, shutdownMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionShutdownMember implements an ShutdownMemberAction. // actionShutdownMember implements an ShutdownMemberAction.
type actionShutdownMember struct { type actionShutdownMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -115,13 +113,3 @@ func (a *actionShutdownMember) CheckProgress(ctx context.Context) (bool, bool, e
// Member still not shutdown, retry soon // Member still not shutdown, retry soon
return false, false, nil return false, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionShutdownMember) Timeout() time.Duration {
return shutdownMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionShutdownMember) MemberID() string {
return a.action.MemberID
}

View file

@ -24,27 +24,29 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewSetCurrentImageAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeSetCurrentImage, newSetCurrentImageAction)
}
// newSetCurrentImageAction creates a new Action that implements the given
// planned SetCurrentImage action. // planned SetCurrentImage action.
func NewSetCurrentImageAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newSetCurrentImageAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &setCurrentImageAction{ a := &setCurrentImageAction{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, upgradeMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// setCurrentImageAction implements an SetCurrentImage. // setCurrentImageAction implements an SetCurrentImage.
type setCurrentImageAction struct { type setCurrentImageAction struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -73,13 +75,3 @@ func (a *setCurrentImageAction) CheckProgress(ctx context.Context) (bool, bool,
log.Info().Str("image", a.action.Image).Msg("Changed current image") log.Info().Str("image", a.action.Image).Msg("Changed current image")
return true, false, nil return true, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *setCurrentImageAction) Timeout() time.Duration {
return upgradeMemberTimeout
}
// Return the MemberID used / created in this action
func (a *setCurrentImageAction) MemberID() string {
return ""
}

View file

@ -24,27 +24,29 @@ package reconcile
import ( import (
"context" "context"
"time"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
// NewUpgradeMemberAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeUpgradeMember, newUpgradeMemberAction)
}
// newUpgradeMemberAction creates a new Action that implements the given
// planned UpgradeMember action. // planned UpgradeMember action.
func NewUpgradeMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newUpgradeMemberAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionUpgradeMember{ a := &actionUpgradeMember{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, upgradeMemberTimeout)
actionCtx: actionCtx,
} return a
} }
// actionUpgradeMember implements an UpgradeMember. // actionUpgradeMember implements an UpgradeMember.
type actionUpgradeMember struct { type actionUpgradeMember struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -128,13 +130,3 @@ func (a *actionUpgradeMember) CheckProgress(ctx context.Context) (bool, bool, er
} }
return isUpgrading, false, nil return isUpgrading, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionUpgradeMember) Timeout() time.Duration {
return upgradeMemberTimeout
}
// Return the MemberID used / created in this action
func (a *actionUpgradeMember) MemberID() string {
return a.action.MemberID
}

View file

@ -24,7 +24,6 @@ package reconcile
import ( import (
"context" "context"
"time"
driver "github.com/arangodb/go-driver" driver "github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/agency" "github.com/arangodb/go-driver/agency"
@ -34,21 +33,24 @@ import (
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1" api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
) )
// NewWaitForMemberUpAction creates a new Action that implements the given func init() {
registerAction(api.ActionTypeWaitForMemberUp, newWaitForMemberUpAction)
}
// newWaitForMemberUpAction creates a new Action that implements the given
// planned WaitForMemberUp action. // planned WaitForMemberUp action.
func NewWaitForMemberUpAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action { func newWaitForMemberUpAction(log zerolog.Logger, action api.Action, actionCtx ActionContext) Action {
return &actionWaitForMemberUp{ a := &actionWaitForMemberUp{}
log: log,
action: action, a.actionImpl = newActionImplDefRef(log, action, actionCtx, waitForMemberUpTimeout)
actionCtx: actionCtx,
} return a
} }
// actionWaitForMemberUp implements an WaitForMemberUp. // actionWaitForMemberUp implements an WaitForMemberUp.
type actionWaitForMemberUp struct { type actionWaitForMemberUp struct {
log zerolog.Logger // actionImpl implement timeout and member id functions
action api.Action actionImpl
actionCtx ActionContext
} }
// Start performs the start of the action. // Start performs the start of the action.
@ -189,13 +191,3 @@ func (a *actionWaitForMemberUp) checkProgressArangoSync(ctx context.Context) (bo
} }
return true, false, nil return true, false, nil
} }
// Timeout returns the amount of time after which this action will timeout.
func (a *actionWaitForMemberUp) Timeout() time.Duration {
return waitForMemberUpTimeout
}
// Return the MemberID used / created in this action
func (a *actionWaitForMemberUp) MemberID() string {
return a.action.MemberID
}

View file

@ -116,4 +116,6 @@ type Context interface {
RenderPodForMember(spec api.DeploymentSpec, status api.DeploymentStatus, memberID string, imageInfo api.ImageInfo) (*v1.Pod, error) RenderPodForMember(spec api.DeploymentSpec, status api.DeploymentStatus, memberID string, imageInfo api.ImageInfo) (*v1.Pod, error)
// SelectImage select currently used image by pod // SelectImage select currently used image by pod
SelectImage(spec api.DeploymentSpec, status api.DeploymentStatus) (api.ImageInfo, bool) SelectImage(spec api.DeploymentSpec, status api.DeploymentStatus) (api.ImageInfo, bool)
// WithStatusUpdate update status of ArangoDeployment with defined modifier. If action returns True action is taken
WithStatusUpdate(action func(s *api.DeploymentStatus) bool, force ...bool) error
} }

View file

@ -44,6 +44,7 @@ import (
) )
var _ PlanBuilderContext = &testContext{} var _ PlanBuilderContext = &testContext{}
var _ Context = &testContext{}
type testContext struct { type testContext struct {
Pods []core.Pod Pods []core.Pod
@ -54,6 +55,10 @@ type testContext struct {
RecordedEvent *k8sutil.Event RecordedEvent *k8sutil.Event
} }
func (c *testContext) WithStatusUpdate(action func(s *api.DeploymentStatus) bool, force ...bool) error {
panic("implement me")
}
func (c *testContext) RenderPodForMember(spec api.DeploymentSpec, status api.DeploymentStatus, memberID string, imageInfo api.ImageInfo) (*core.Pod, error) { func (c *testContext) RenderPodForMember(spec api.DeploymentSpec, status api.DeploymentStatus, memberID string, imageInfo api.ImageInfo) (*core.Pod, error) {
panic("implement me") panic("implement me")
} }

View file

@ -157,47 +157,14 @@ func (d *Reconciler) ExecutePlan(ctx context.Context) (bool, error) {
} }
} }
// startAction performs the start of the given action // createAction create action object based on action type
// 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 (d *Reconciler) createAction(ctx context.Context, log zerolog.Logger, action api.Action) Action { func (d *Reconciler) createAction(ctx context.Context, log zerolog.Logger, action api.Action) Action {
actionCtx := newActionContext(log, d.context) actionCtx := newActionContext(log, d.context)
switch action.Type {
case api.ActionTypeAddMember: f, ok := getActionFactory(action.Type)
return NewAddMemberAction(log, action, actionCtx) if !ok {
case api.ActionTypeRemoveMember:
return NewRemoveMemberAction(log, action, actionCtx)
case api.ActionTypeRecreateMember:
return NewRecreateMemberAction(log, action, actionCtx)
case api.ActionTypeCleanOutMember:
return NewCleanOutMemberAction(log, action, actionCtx)
case api.ActionTypeShutdownMember:
return NewShutdownMemberAction(log, action, actionCtx)
case api.ActionTypeRotateMember:
return NewRotateMemberAction(log, action, actionCtx)
case api.ActionTypeRotateStartMember:
return NewRotateStartMemberAction(log, action, actionCtx)
case api.ActionTypeRotateStopMember:
return NewRotateStopMemberAction(log, action, actionCtx)
case api.ActionTypeUpgradeMember:
return NewUpgradeMemberAction(log, action, actionCtx)
case api.ActionTypeWaitForMemberUp:
return NewWaitForMemberUpAction(log, action, actionCtx)
case api.ActionTypeRenewTLSCertificate:
return NewRenewTLSCertificateAction(log, action, actionCtx)
case api.ActionTypeRenewTLSCACertificate:
return NewRenewTLSCACertificateAction(log, action, actionCtx)
case api.ActionTypeSetCurrentImage:
return NewSetCurrentImageAction(log, action, actionCtx)
case api.ActionTypeDisableClusterScaling:
return NewDisableScalingCluster(log, action, actionCtx)
case api.ActionTypeEnableClusterScaling:
return NewEnableScalingCluster(log, action, actionCtx)
case api.ActionTypePVCResize:
return NewPVCResizeAction(log, action, actionCtx)
case api.ActionTypePVCResized:
return NewPVCResizedAction(log, action, actionCtx)
default:
panic(fmt.Sprintf("Unknown action type '%s'", action.Type)) panic(fmt.Sprintf("Unknown action type '%s'", action.Type))
} }
return f(log, action, actionCtx)
} }

View file

@ -37,4 +37,7 @@ const (
shutdownMemberTimeout = time.Minute * 30 shutdownMemberTimeout = time.Minute * 30
upgradeMemberTimeout = time.Hour * 6 upgradeMemberTimeout = time.Hour * 6
waitForMemberUpTimeout = time.Minute * 15 waitForMemberUpTimeout = time.Minute * 15
upToDateUpdateTimeout = time.Minute
shutdownTimeout = time.Second * 15
) )

View file

@ -92,4 +92,6 @@ type Context interface {
GetDatabaseClient(ctx context.Context) (driver.Client, error) GetDatabaseClient(ctx context.Context) (driver.Client, error)
// GetAgency returns a connection to the entire agency. // GetAgency returns a connection to the entire agency.
GetAgency(ctx context.Context) (agency.Agency, error) GetAgency(ctx context.Context) (agency.Agency, error)
// WithStatusUpdate update status of ArangoDeployment with defined modifier. If action returns True action is taken
WithStatusUpdate(action func(s *api.DeploymentStatus) bool, force ...bool) error
} }