mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
Restore immutable fields
This commit is contained in:
parent
b9fc6db898
commit
a90a14562b
3 changed files with 150 additions and 9 deletions
|
@ -361,6 +361,23 @@ func (s *ServerGroupSpec) SetDefaults(group ServerGroup, used bool, mode Deploym
|
|||
}
|
||||
}
|
||||
|
||||
// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec.
|
||||
// It returns a list of fields that have been reset.
|
||||
func (s ServerGroupSpec) ResetImmutableFields(group ServerGroup, fieldPrefix string, target *ServerGroupSpec) []string {
|
||||
var resetFields []string
|
||||
if group == ServerGroupAgents {
|
||||
if s.Count != target.Count {
|
||||
target.Count = s.Count
|
||||
resetFields = append(resetFields, fieldPrefix+".count")
|
||||
}
|
||||
}
|
||||
if s.StorageClassName != target.StorageClassName {
|
||||
target.StorageClassName = s.StorageClassName
|
||||
resetFields = append(resetFields, fieldPrefix+".storageClassName")
|
||||
}
|
||||
return resetFields
|
||||
}
|
||||
|
||||
// DeploymentSpec contains the spec part of a ArangoDeployment resource.
|
||||
type DeploymentSpec struct {
|
||||
Mode DeploymentMode `json:"mode,omitempty"`
|
||||
|
@ -476,3 +493,37 @@ func (s *DeploymentSpec) Validate() error {
|
|||
func (s DeploymentSpec) IsDevelopment() bool {
|
||||
return s.Environment == EnvironmentDevelopment
|
||||
}
|
||||
|
||||
// ResetImmutableFields replaces all immutable fields in the given target with values from the source spec.
|
||||
// It returns a list of fields that have been reset.
|
||||
// Field names are relative to `spec.`.
|
||||
func (s DeploymentSpec) ResetImmutableFields(target *DeploymentSpec) []string {
|
||||
var resetFields []string
|
||||
if s.Mode != target.Mode {
|
||||
target.Mode = s.Mode
|
||||
resetFields = append(resetFields, "mode")
|
||||
}
|
||||
if s.StorageEngine != target.StorageEngine {
|
||||
target.StorageEngine = s.StorageEngine
|
||||
resetFields = append(resetFields, "storageEngine")
|
||||
}
|
||||
if l := s.Single.ResetImmutableFields(ServerGroupSingle, "single", &target.Single); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
if l := s.Agents.ResetImmutableFields(ServerGroupAgents, "agents", &target.Agents); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
if l := s.DBServers.ResetImmutableFields(ServerGroupDBServers, "dbservers", &target.DBServers); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
if l := s.Coordinators.ResetImmutableFields(ServerGroupCoordinators, "coordinators", &target.Coordinators); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
if l := s.SyncMasters.ResetImmutableFields(ServerGroupSyncMasters, "syncmasters", &target.SyncMasters); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
if l := s.SyncWorkers.ResetImmutableFields(ServerGroupSyncWorkers, "syncworkers", &target.SyncWorkers); l != nil {
|
||||
resetFields = append(resetFields, l...)
|
||||
}
|
||||
return resetFields
|
||||
}
|
||||
|
|
|
@ -218,7 +218,36 @@ func (d *Deployment) run() {
|
|||
|
||||
// handleArangoDeploymentUpdatedEvent is called when the deployment is updated by the user.
|
||||
func (d *Deployment) handleArangoDeploymentUpdatedEvent(event *deploymentEvent) error {
|
||||
// TODO
|
||||
log := d.deps.Log.With().Str("deployment", event.Deployment.GetName()).Logger()
|
||||
|
||||
newAPIObject := event.Deployment.DeepCopy()
|
||||
newAPIObject.Spec.SetDefaults()
|
||||
newAPIObject.Status = d.status
|
||||
resetFields := d.apiObject.Spec.ResetImmutableFields(&newAPIObject.Spec)
|
||||
if len(resetFields) > 0 {
|
||||
log.Debug().Strs("fields", resetFields).Msg("Found modified immutable fields")
|
||||
}
|
||||
if err := newAPIObject.Spec.Validate(); err != nil {
|
||||
d.createEvent(k8sutil.NewErrorEvent("Validation failed", err, d.apiObject))
|
||||
// Try to reset object
|
||||
if err := d.updateCRSpec(d.apiObject.Spec); err != nil {
|
||||
log.Error().Err(err).Msg("Restore original spec failed")
|
||||
d.createEvent(k8sutil.NewErrorEvent("Restore original failed", err, d.apiObject))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if len(resetFields) > 0 {
|
||||
for _, fieldName := range resetFields {
|
||||
log.Debug().Str("field", fieldName).Msg("Reset immutable field")
|
||||
d.createEvent(k8sutil.NewImmutableFieldEvent(fieldName, d.apiObject))
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated spec
|
||||
if err := d.updateCRSpec(newAPIObject.Spec); err != nil {
|
||||
return maskAny(fmt.Errorf("failed to update ArangoDeployment spec: %v", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -240,16 +269,67 @@ func (d *Deployment) updateCRStatus() error {
|
|||
|
||||
// Send update to API server
|
||||
update := d.apiObject.DeepCopy()
|
||||
update.Status = d.status
|
||||
newAPIObject, err := d.deps.DatabaseCRCli.DatabaseV1alpha().ArangoDeployments(d.apiObject.Namespace).Update(update)
|
||||
if err != nil {
|
||||
return maskAny(fmt.Errorf("failed to update ArangoDeployment status: %v", err))
|
||||
attempt := 0
|
||||
for {
|
||||
attempt++
|
||||
update.Status = d.status
|
||||
ns := d.apiObject.GetNamespace()
|
||||
newAPIObject, err := d.deps.DatabaseCRCli.DatabaseV1alpha().ArangoDeployments(ns).Update(update)
|
||||
if err == nil {
|
||||
// Update internal object
|
||||
d.apiObject = newAPIObject
|
||||
return nil
|
||||
}
|
||||
if attempt < 10 && k8sutil.IsConflict(err) {
|
||||
// API object may have been changed already,
|
||||
// Reload api object and try again
|
||||
var current *api.ArangoDeployment
|
||||
current, err = d.deps.DatabaseCRCli.DatabaseV1alpha().ArangoDeployments(ns).Get(update.GetName(), metav1.GetOptions{})
|
||||
if err == nil {
|
||||
update = current.DeepCopy()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
d.deps.Log.Debug().Err(err).Msg("failed to patch ArangoDeployment status")
|
||||
return maskAny(fmt.Errorf("failed to patch ArangoDeployment status: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update internal object
|
||||
d.apiObject = newAPIObject
|
||||
|
||||
return nil
|
||||
// Update the spec part of the API object (d.apiObject)
|
||||
// to the given object, while preserving the status.
|
||||
// On success, d.apiObject is updated.
|
||||
func (d *Deployment) updateCRSpec(newSpec api.DeploymentSpec) error {
|
||||
// Send update to API server
|
||||
update := d.apiObject.DeepCopy()
|
||||
attempt := 0
|
||||
for {
|
||||
attempt++
|
||||
update.Spec = newSpec
|
||||
update.Status = d.status
|
||||
ns := d.apiObject.GetNamespace()
|
||||
newAPIObject, err := d.deps.DatabaseCRCli.DatabaseV1alpha().ArangoDeployments(ns).Update(update)
|
||||
if err == nil {
|
||||
// Update internal object
|
||||
d.apiObject = newAPIObject
|
||||
return nil
|
||||
}
|
||||
if attempt < 10 && k8sutil.IsConflict(err) {
|
||||
// API object may have been changed already,
|
||||
// Reload api object and try again
|
||||
var current *api.ArangoDeployment
|
||||
current, err = d.deps.DatabaseCRCli.DatabaseV1alpha().ArangoDeployments(ns).Get(update.GetName(), metav1.GetOptions{})
|
||||
if err == nil {
|
||||
update = current.DeepCopy()
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
d.deps.Log.Debug().Err(err).Msg("failed to patch ArangoDeployment spec")
|
||||
return maskAny(fmt.Errorf("failed to patch ArangoDeployment spec: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// failOnError reports the given error and sets the deployment status to failed.
|
||||
|
|
|
@ -68,6 +68,16 @@ func NewPodGoneEvent(podName, role string, apiObject APIObject) *v1.Event {
|
|||
return event
|
||||
}
|
||||
|
||||
// NewImmutableFieldEvent creates an event indicating that an attempt was made to change a field
|
||||
// that is immutable.
|
||||
func NewImmutableFieldEvent(fieldName string, apiObject APIObject) *v1.Event {
|
||||
event := newDeploymentEvent(apiObject)
|
||||
event.Type = v1.EventTypeNormal
|
||||
event.Reason = "Immutable Field Change"
|
||||
event.Message = fmt.Sprintf("Changing field %s is not possible. It has been reset to its original value.", fieldName)
|
||||
return event
|
||||
}
|
||||
|
||||
// NewErrorEvent creates an even of type error.
|
||||
func NewErrorEvent(reason string, err error, apiObject APIObject) *v1.Event {
|
||||
event := newDeploymentEvent(apiObject)
|
||||
|
|
Loading…
Reference in a new issue