Skip to content
This repository has been archived by the owner on Mar 6, 2024. It is now read-only.

Commit

Permalink
add paused flag to catapult (#497)
Browse files Browse the repository at this point in the history
* add `paused` flag to catapult

* fix lint errors

* change paused flat to string

* camelCase true/false paused flags
  • Loading branch information
aborilov committed Jul 6, 2020
1 parent ee0fcd1 commit 1a3e9cb
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 21 deletions.
Expand Up @@ -64,6 +64,10 @@ spec:
- plural
- version
type: object
paused:
description: Paused tell controller to pause reconciliation process
and assume that Catapult is ready
type: string
serviceCluster:
description: References the ServiceCluster object that this object
belongs to.
Expand Down
106 changes: 91 additions & 15 deletions pkg/apis/operator/v1alpha1/catapult_types.go
Expand Up @@ -36,8 +36,23 @@ type CatapultSpec struct {
// ServiceCluster: Webhook will call webhooks of the CRD in the ServiceCluster with dry-run flag.
// +kubebuilder:default:=None
WebhookStrategy corev1alpha1.WebhookStrategyType `json:"webhookStrategy,omitempty"`
// Paused tell controller to pause reconciliation process and assume that Catapult is ready
Paused PausedFlagType `json:"paused,omitempty"`
}

// PausedFlagType represents a enable/disable flag
type PausedFlagType string

func (o PausedFlagType) IsPaused() bool {
return o == PausedFlagTrue
}

// Values of PausedFlagType.
const (
PausedFlagTrue PausedFlagType = "True"
PausedFlagFalse PausedFlagType = "False"
)

// CatapultStatus defines the observed state of Catapult.
type CatapultStatus struct {
// ObservedGeneration is the most recent generation observed for this Catapult by the controller.
Expand All @@ -58,6 +73,7 @@ type CatapultPhaseType string
// Values of CatapultPhaseType.
const (
CatapultPhaseReady CatapultPhaseType = "Ready"
CatapultPhasePaused CatapultPhaseType = "Paused"
CatapultPhaseNotReady CatapultPhaseType = "NotReady"
CatapultPhaseUnknown CatapultPhaseType = "Unknown"
CatapultPhaseTerminating CatapultPhaseType = "Terminating"
Expand All @@ -72,26 +88,28 @@ const (
func (s *CatapultStatus) updatePhase() {

for _, condition := range s.Conditions {
if condition.Type != CatapultReady {
continue

if condition.Type == CatapultPaused && condition.Status == ConditionTrue {
s.Phase = CatapultPhasePaused
return
}

switch condition.Status {
case ConditionTrue:
s.Phase = CatapultPhaseReady
case ConditionFalse:
if condition.Reason == CatapultTerminatingReason {
s.Phase = CatapultPhaseTerminating
} else {
s.Phase = CatapultPhaseNotReady
if condition.Type == CatapultReady {

switch condition.Status {
case ConditionTrue:
s.Phase = CatapultPhaseReady
case ConditionFalse:
if condition.Reason == CatapultTerminatingReason {
s.Phase = CatapultPhaseTerminating
} else {
s.Phase = CatapultPhaseNotReady
}
case ConditionUnknown:
s.Phase = CatapultPhaseUnknown
}
case ConditionUnknown:
s.Phase = CatapultPhaseUnknown
}
return
}

s.Phase = CatapultPhaseUnknown
}

// CatapultConditionType represents a CatapultCondition value.
Expand All @@ -100,6 +118,8 @@ type CatapultConditionType string
const (
// CatapultReady represents a Catapult condition is in ready state.
CatapultReady CatapultConditionType = "Ready"
// CatapultPaused represents a Catapult condition is in paused state.
CatapultPaused CatapultConditionType = "Paused"
)

// CatapultCondition contains details for the current condition of this Catapult.
Expand Down Expand Up @@ -191,6 +211,21 @@ func (s *Catapult) IsReady() bool {
return false
}

// IsPaused returns if the Catapult is paused.
func (s *Catapult) IsPaused() bool {
if s.Generation != s.Status.ObservedGeneration {
return false
}

for _, condition := range s.Status.Conditions {
if condition.Type == CatapultPaused &&
condition.Status == ConditionTrue {
return true
}
}
return false
}

func (s *Catapult) SetReadyCondition() bool {
if !s.IsReady() {
s.Status.ObservedGeneration = s.Generation
Expand All @@ -204,6 +239,47 @@ func (s *Catapult) SetReadyCondition() bool {
}
return false
}

func (s *Catapult) SetPausedCondition() bool {
var changed bool
if !s.IsReady() {
changed = true
s.Status.ObservedGeneration = s.Generation
s.Status.SetCondition(CatapultCondition{
Type: CatapultReady,
Status: ConditionTrue,
Reason: "Paused",
Message: "Reconcilation is paused, assuming component is ready.",
})
return changed
}
if !s.IsPaused() {
changed = true
s.Status.ObservedGeneration = s.Generation
s.Status.SetCondition(CatapultCondition{
Type: CatapultPaused,
Status: ConditionTrue,
Reason: "Paused",
Message: "Reconcilation is paused",
})
}
return changed
}

func (s *Catapult) SetUnPausedCondition() bool {
if s.IsPaused() {
s.Status.ObservedGeneration = s.Generation
s.Status.SetCondition(CatapultCondition{
Type: CatapultPaused,
Status: ConditionFalse,
Reason: "UnPaused",
Message: "Reconcilation is resumed",
})
return true
}
return false
}

func (s *Catapult) SetUnReadyCondition() bool {
readyCondition, _ := s.Status.GetCondition(CatapultReady)
if readyCondition.Status != ConditionFalse {
Expand Down
2 changes: 1 addition & 1 deletion pkg/apiserver/api/v1/validation.go
Expand Up @@ -106,7 +106,7 @@ func validateSpec(req SpecGetter) error {
if req.GetSpec() == nil {
return fmt.Errorf("missing spec")
}
if req.GetSpec().Metadata == nil && req.GetSpec().Metadata.Name == "" {
if req.GetSpec().Metadata == nil || req.GetSpec().Metadata.Name == "" {
return fmt.Errorf("missing metadata name")
}
return nil
Expand Down
4 changes: 4 additions & 0 deletions pkg/internal/resources/operator/operator.golden.yaml
Expand Up @@ -287,6 +287,10 @@
- plural
- version
type: object
paused:
description: Paused tell controller to pause reconciliation process
and assume that Catapult is ready
type: string
serviceCluster:
description: References the ServiceCluster object that this object
belongs to.
Expand Down
2 changes: 1 addition & 1 deletion pkg/internal/resources/operator/statik.go

Large diffs are not rendered by default.

Expand Up @@ -276,6 +276,8 @@ func (r *CustomResourceDiscoveryReconciler) reconcileCatapult(
return desiredCatapult, nil
}

// leave `Paused` flag as is
desiredCatapult.Spec.Paused = currentCatapult.Spec.Paused
// Update Catapult
currentCatapult.Spec = desiredCatapult.Spec
if err = r.Update(ctx, currentCatapult); err != nil {
Expand Down
21 changes: 17 additions & 4 deletions pkg/operator/internal/controllers/catapult_controller.go
Expand Up @@ -73,6 +73,15 @@ func (r *CatapultReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
if err := r.Get(ctx, req.NamespacedName, catapult); err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if catapult.Spec.Paused.IsPaused() {
if catapult.SetPausedCondition() {
if err := r.Client.Status().Update(ctx, catapult); err != nil {
return ctrl.Result{}, fmt.Errorf("updating %s status: %w", catapult.Name, err)
}
}
// reconciliation paused, skip all other handlers
return ctrl.Result{}, nil
}

if !catapult.DeletionTimestamp.IsZero() {
if err := r.handleDeletion(ctx, catapult); err != nil {
Expand Down Expand Up @@ -175,15 +184,19 @@ func (r *CatapultReconciler) handleDeletion(ctx context.Context, catapult *opera
}

func (r *CatapultReconciler) updateStatus(ctx context.Context, catapult *operatorv1alpha1.Catapult, deploymentIsReady bool) error {
var statusChanged bool
var pausedChanged, readyChanged bool

if !catapult.Spec.Paused.IsPaused() {
pausedChanged = catapult.SetUnPausedCondition()
}

if deploymentIsReady {
statusChanged = catapult.SetReadyCondition()
readyChanged = catapult.SetReadyCondition()
} else {
statusChanged = catapult.SetUnReadyCondition()
readyChanged = catapult.SetUnReadyCondition()
}

if statusChanged {
if readyChanged || pausedChanged {
if err := r.Client.Status().Update(ctx, catapult); err != nil {
return fmt.Errorf("updating %s status: %w", catapult.Name, err)
}
Expand Down
20 changes: 20 additions & 0 deletions test/integration/servicecluster.go
Expand Up @@ -35,6 +35,7 @@ import (
catalogv1alpha1 "github.com/kubermatic/kubecarrier/pkg/apis/catalog/v1alpha1"
corev1alpha1 "github.com/kubermatic/kubecarrier/pkg/apis/core/v1alpha1"
fakev1 "github.com/kubermatic/kubecarrier/pkg/apis/fake/v1"
operatorv1alpha1 "github.com/kubermatic/kubecarrier/pkg/apis/operator/v1alpha1"
"github.com/kubermatic/kubecarrier/pkg/testutil"
)

Expand Down Expand Up @@ -153,6 +154,25 @@ func newServiceClusterSuite(

t.Log("CatalogEntry & CustomResourceDiscoverySet exists")

// Check Catapult
catapult := &operatorv1alpha1.Catapult{}
require.NoError(t, managementClient.Get(ctx, types.NamespacedName{
Name: catalogEntrySet.Name + "." + serviceCluster.Name,
Namespace: catalogEntrySet.Namespace,
}, catapult), "getting catapult")

t.Log("Catapult exists")
catapult.Spec.Paused = operatorv1alpha1.PausedFlagTrue
require.NoError(t, managementClient.Update(ctx, catapult), "set catapult paused flag to true")
require.NoError(t, testutil.WaitUntilCondition(ctx, managementClient, catapult, operatorv1alpha1.CatapultPaused, operatorv1alpha1.ConditionTrue))
require.NoError(t, testutil.WaitUntilReady(ctx, managementClient, catapult))
require.Equal(t, operatorv1alpha1.CatapultPhasePaused, catapult.Status.Phase)

catapult.Spec.Paused = operatorv1alpha1.PausedFlagFalse
require.NoError(t, managementClient.Update(ctx, catapult), "set catapult paused flag to true")
require.NoError(t, testutil.WaitUntilCondition(ctx, managementClient, catapult, operatorv1alpha1.CatapultPaused, operatorv1alpha1.ConditionFalse))
require.Equal(t, operatorv1alpha1.CatapultPhaseReady, catapult.Status.Phase)

// Check the Catapult dynamic webhook service is deployed.
webhookService := &corev1.Service{}
assert.NoError(t, managementClient.Get(ctx, types.NamespacedName{
Expand Down

0 comments on commit 1a3e9cb

Please sign in to comment.