Skip to content

Commit

Permalink
[deckhouse-controller] Separate deckhouse release reconcile process f…
Browse files Browse the repository at this point in the history
…rom generic updater (#11129)

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Mikhail Scherba <mikhail.scherba@flant.com>
Signed-off-by: Mikhail Scherba <41360396+miklezzzz@users.noreply.github.com>
Co-authored-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Co-authored-by: Mikhail Scherba <mikhail.scherba@flant.com>
Co-authored-by: Mikhail Scherba <41360396+miklezzzz@users.noreply.github.com>
  • Loading branch information
4 people authored Jan 28, 2025
1 parent bef10bc commit b8e8409
Show file tree
Hide file tree
Showing 85 changed files with 3,159 additions and 671 deletions.
2 changes: 2 additions & 0 deletions deckhouse-controller/cmd/deckhouse-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
sh_debug "github.com/flant/shell-operator/pkg/debug"
"gopkg.in/alecthomas/kingpin.v2"

"github.com/deckhouse/deckhouse/deckhouse-controller/pkg/controller"
"github.com/deckhouse/deckhouse/deckhouse-controller/pkg/debug"
"github.com/deckhouse/deckhouse/deckhouse-controller/pkg/helpers"
"github.com/deckhouse/deckhouse/deckhouse-controller/pkg/registry"
Expand Down Expand Up @@ -58,6 +59,7 @@ const (
func main() {
sh_app.Version = ShellOperatorVersion
ad_app.Version = AddonOperatorVersion
controller.DeckhouseVersion = DeckhouseVersion

FileName := filepath.Base(os.Args[0])

Expand Down
22 changes: 0 additions & 22 deletions deckhouse-controller/pkg/apis/deckhouse.io/v1alpha1/annotation.go

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
DeckhouseReleasePhasePending = "Pending"
DeckhouseReleasePhaseDeployed = "Deployed"
DeckhouseReleasePhaseSuperseded = "Superseded"
DeckhouseReleasePhaseSuspended = "Suspended"
DeckhouseReleasePhaseSkipped = "Skipped"

DeckhouseReleaseApprovalAnnotation = "release.deckhouse.io/approved"
DeckhouseReleaseAnnotationIsUpdating = "release.deckhouse.io/isUpdating"
DeckhouseReleaseAnnotationNotified = "release.deckhouse.io/notified"
DeckhouseReleaseAnnotationApplyNow = "release.deckhouse.io/apply-now"
DeckhouseReleaseAnnotationApplyAfter = "release.deckhouse.io/applyAfter"
DeckhouseReleaseAnnotationDisruptionApproved = "release.deckhouse.io/disruption-approved"
DeckhouseReleaseAnnotationForce = "release.deckhouse.io/force"
DeckhouseReleaseAnnotationSuspended = "release.deckhouse.io/suspended"
DeckhouseReleaseAnnotationNotificationTimeShift = "release.deckhouse.io/notification-time-shift"
DeckhouseReleaseAnnotationDryrun = "dryrun"
DeckhouseReleaseAnnotationTriggeredByDryrun = "triggered_by_dryrun"
DeckhouseReleaseAnnotationCurrentRestored = "release.deckhouse.io/current-restored"

// TODO: remove in entire code
DeckhouseReleaseAnnotationCooldown = "release.deckhouse.io/cooldown"
)

var DeckhouseReleaseGVK = schema.GroupVersionKind{
Group: SchemeGroupVersion.Group,
Version: SchemeGroupVersion.Version,
Expand Down Expand Up @@ -69,9 +93,10 @@ func (in *DeckhouseRelease) GetChangelogLink() string {
return in.Spec.ChangelogLink
}

// TODO: remove cooldown from entire code
func (in *DeckhouseRelease) GetCooldownUntil() *time.Time {
cooldown := new(time.Time)
if v, ok := in.Annotations["release.deckhouse.io/cooldown"]; ok {
if v, ok := in.Annotations[DeckhouseReleaseAnnotationCooldown]; ok {
cd, err := time.Parse(time.RFC3339, v)
if err == nil {
cooldown = &cd
Expand All @@ -86,7 +111,7 @@ func (in *DeckhouseRelease) GetDisruptions() []string {
}

func (in *DeckhouseRelease) GetDisruptionApproved() bool {
v, ok := in.Annotations["release.deckhouse.io/disruption-approved"]
v, ok := in.Annotations[DeckhouseReleaseAnnotationDisruptionApproved]
return ok && v == "true"
}

Expand All @@ -95,12 +120,22 @@ func (in *DeckhouseRelease) GetPhase() string {
}

func (in *DeckhouseRelease) GetForce() bool {
v, ok := in.Annotations["release.deckhouse.io/force"]
v, ok := in.Annotations[DeckhouseReleaseAnnotationForce]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetApplyNow() bool {
v, ok := in.Annotations["release.deckhouse.io/apply-now"]
v, ok := in.Annotations[DeckhouseReleaseAnnotationApplyNow]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetIsUpdating() bool {
v, ok := in.Annotations[DeckhouseReleaseAnnotationIsUpdating]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetNotified() bool {
v, ok := in.Annotations[DeckhouseReleaseAnnotationNotified]
return ok && v == "true"
}

Expand All @@ -113,7 +148,7 @@ func (in *DeckhouseRelease) SetApprovedStatus(val bool) {
}

func (in *DeckhouseRelease) GetSuspend() bool {
v, ok := in.Annotations["release.deckhouse.io/suspended"]
v, ok := in.Annotations[DeckhouseReleaseAnnotationSuspended]
return ok && v == "true"
}

Expand All @@ -123,19 +158,30 @@ func (in *DeckhouseRelease) GetManuallyApproved() bool {
}

v, ok := in.Annotations[DeckhouseReleaseApprovalAnnotation]
if ok {
return v == "true"
}

return in.Approved
return ok && v == "true"
}

func (in *DeckhouseRelease) GetMessage() string {
return in.Status.Message
}

func (in *DeckhouseRelease) GetNotificationShift() bool {
v, ok := in.Annotations["release.deckhouse.io/notification-time-shift"]
v, ok := in.Annotations[DeckhouseReleaseAnnotationNotificationTimeShift]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetDryRun() bool {
v, ok := in.Annotations[DeckhouseReleaseAnnotationDryrun]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetTriggeredByDryRun() bool {
v, ok := in.Annotations[DeckhouseReleaseAnnotationTriggeredByDryrun]
return ok && v == "true"
}

func (in *DeckhouseRelease) GetCurrentRestored() bool {
v, ok := in.Annotations[DeckhouseReleaseAnnotationCurrentRestored]
return ok && v == "true"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const (
ModuleReleasePhaseSuspended = "Suspended"
ModuleReleasePhaseSkipped = "Skipped"

ModuleReleaseApprovalAnnotation = "modules.deckhouse.io/approved"

ModuleReleaseAnnotationApplyNow = "release.deckhouse.io/apply-now"

ModuleReleaseAnnotationRegistrySpecChanged = "modules.deckhouse.io/registry-spec-changed"
Expand Down
4 changes: 3 additions & 1 deletion deckhouse-controller/pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import (
"github.com/deckhouse/deckhouse/pkg/log"
)

var DeckhouseVersion string

const (
docsLeaseLabel = "deckhouse.io/documentation-builder-sync"

Expand Down Expand Up @@ -225,7 +227,7 @@ func NewDeckhouseController(ctx context.Context, version string, operator *addon
loader := moduleloader.New(runtimeManager.GetClient(), version, operator.ModuleManager.ModulesDir, operator.ModuleManager.GlobalHooksDir, dc, embeddedPolicy, logger.Named("module-loader"))
operator.ModuleManager.SetModuleLoader(loader)

err = deckhouserelease.NewDeckhouseReleaseController(ctx, runtimeManager, dc, operator.ModuleManager, settingsContainer, operator.MetricStorage, preflightCountDown, logger.Named("deckhouse-release-controller"))
err = deckhouserelease.NewDeckhouseReleaseController(ctx, runtimeManager, dc, operator.ModuleManager, settingsContainer, operator.MetricStorage, preflightCountDown, DeckhouseVersion, logger.Named("deckhouse-release-controller"))
if err != nil {
return nil, fmt.Errorf("create deckhouse release controller: %w", err)
}
Expand Down
50 changes: 50 additions & 0 deletions deckhouse-controller/pkg/controller/ctrlutils/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2024 Flant JSC
//
// 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.

package ctrlutils

import (
"k8s.io/apimachinery/pkg/util/wait"
)

type UpdateOption func(optionsApplier UpdateOptionApplier)

func (opt UpdateOption) Apply(o UpdateOptionApplier) {
opt(o)
}

type UpdateOptionApplier interface {
WithOnErrorBackoff(b *wait.Backoff)
WithRetryOnConflictBackoff(b *wait.Backoff)

withStatusUpdate()
}

func WithOnErrorBackoff(b *wait.Backoff) UpdateOption {
return func(optionsApplier UpdateOptionApplier) {
optionsApplier.WithOnErrorBackoff(b)
}
}

func WithRetryOnConflictBackoff(b *wait.Backoff) UpdateOption {
return func(optionsApplier UpdateOptionApplier) {
optionsApplier.WithRetryOnConflictBackoff(b)
}
}

func withStatusUpdate() UpdateOption {
return func(optionsApplier UpdateOptionApplier) {
optionsApplier.withStatusUpdate()
}
}
112 changes: 112 additions & 0 deletions deckhouse-controller/pkg/controller/ctrlutils/update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2024 Flant JSC
//
// 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.

package ctrlutils

import (
"context"
"errors"

"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type MutateFn func() error

type UpdateOptions struct {
OnErrorBackoff wait.Backoff
RetryOnConflictBackoff wait.Backoff

statusUpdate bool
}

func (o *UpdateOptions) WithOnErrorBackoff(b *wait.Backoff) {
if b == nil {
return
}

o.OnErrorBackoff = *b
}

func (o *UpdateOptions) WithRetryOnConflictBackoff(b *wait.Backoff) {
if b == nil {
return
}

o.RetryOnConflictBackoff = *b
}

func (o *UpdateOptions) withStatusUpdate() {
o.statusUpdate = true
}

var ErrCanNotMutateNameOrNamespace = errors.New("MutateFn cannot mutate object name and/or object namespace")

func UpdateWithRetry(ctx context.Context, c client.Client, obj client.Object, f MutateFn, opts ...UpdateOption) error {
options := &UpdateOptions{
OnErrorBackoff: retry.DefaultRetry,
RetryOnConflictBackoff: retry.DefaultRetry,
}

for _, fn := range opts {
fn.Apply(options)
}

key := client.ObjectKeyFromObject(obj)

return retry.OnError(options.OnErrorBackoff, apierrors.IsServiceUnavailable, func() error {
return retry.RetryOnConflict(options.RetryOnConflictBackoff, func() error {
if err := c.Get(ctx, key, obj); err != nil {
return client.IgnoreNotFound(err)
}

existing := obj.DeepCopyObject()
if err := mutate(f, key, obj); err != nil {
return err
}

if equality.Semantic.DeepEqual(existing, obj) {
return nil
}

if options.statusUpdate {
return c.Status().Update(ctx, obj)
}

return c.Update(ctx, obj)
})
})
}

func UpdateStatusWithRetry(ctx context.Context, c client.Client, obj client.Object, f MutateFn, opts ...UpdateOption) error {
opts = append(opts, withStatusUpdate())

return UpdateWithRetry(ctx, c, obj, f, opts...)
}

// mutate wraps a MutateFn and applies validation to its result.
func mutate(f MutateFn, key client.ObjectKey, obj client.Object) error {
if err := f(); err != nil {
return err
}

if newKey := client.ObjectKeyFromObject(obj); key != newKey {
return ErrCanNotMutateNameOrNamespace
}

return nil
}
Loading

0 comments on commit b8e8409

Please sign in to comment.