mirror of https://github.com/fluxcd/flagger.git
167 lines
5.4 KiB
Go
167 lines
5.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/util/retry"
|
|
|
|
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
|
|
)
|
|
|
|
const finalizer = "finalizer.flagger.app"
|
|
|
|
func (c *Controller) finalize(old interface{}) error {
|
|
canary, ok := old.(*flaggerv1.Canary)
|
|
if !ok {
|
|
return fmt.Errorf("received unexpected object: %v", old)
|
|
}
|
|
|
|
_, err := c.flaggerClient.FlaggerV1beta1().Canaries(canary.Namespace).Get(context.TODO(), canary.Name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("get query error: %w", err)
|
|
}
|
|
|
|
// Retrieve a controller
|
|
canaryController := c.canaryFactory.Controller(canary.Spec.TargetRef.Kind)
|
|
|
|
// Set the status to terminating if not already in that state
|
|
if canary.Status.Phase != flaggerv1.CanaryPhaseTerminating {
|
|
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhaseTerminating); err != nil {
|
|
return fmt.Errorf("failed to update status: %w", err)
|
|
}
|
|
|
|
// record event
|
|
c.recordEventInfof(canary, "Terminating canary %s.%s", canary.Name, canary.Namespace)
|
|
}
|
|
|
|
// Revert the Kubernetes deployment or daemonset
|
|
err = canaryController.Finalize(canary)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to revert target: %w", err)
|
|
}
|
|
c.logger.Infof("%s.%s kind %s reverted", canary.Name, canary.Namespace, canary.Spec.TargetRef.Kind)
|
|
|
|
// Ensure that targetRef has met a ready state
|
|
c.logger.Infof("Checking if canary is ready %s.%s", canary.Name, canary.Namespace)
|
|
_, err = canaryController.IsCanaryReady(canary)
|
|
if err != nil {
|
|
return fmt.Errorf("canary not ready during finalizing: %w", err)
|
|
}
|
|
|
|
labelSelector, labelValue, ports, err := canaryController.GetMetadata(canary)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get metadata for router finalizing: %w", err)
|
|
}
|
|
|
|
// Revert the Kubernetes service
|
|
router := c.routerFactory.KubernetesRouter(canary.Spec.TargetRef.Kind, labelSelector, labelValue, ports)
|
|
if err := router.Finalize(canary); err != nil {
|
|
return fmt.Errorf("failed revert router: %w", err)
|
|
}
|
|
c.logger.Infof("%s.%s router reverted", canary.Name, canary.Namespace)
|
|
|
|
// Revert the mesh objects
|
|
if err := c.revertMesh(canary); err != nil {
|
|
return fmt.Errorf("failed to revert mesh: %w", err)
|
|
}
|
|
|
|
c.logger.Infof("Finalization complete for %s.%s", canary.Name, canary.Namespace)
|
|
return nil
|
|
}
|
|
|
|
// revertMesh reverts defined mesh provider based upon the implementation's respective Finalize method.
|
|
// If the Finalize method encounters and error that is returned, else revert is considered successful.
|
|
func (c *Controller) revertMesh(r *flaggerv1.Canary) error {
|
|
provider := c.meshProvider
|
|
if r.Spec.Provider != "" {
|
|
provider = r.Spec.Provider
|
|
}
|
|
|
|
meshRouter := c.routerFactory.MeshRouter(provider, "")
|
|
if err := meshRouter.Finalize(r); err != nil {
|
|
return fmt.Errorf("meshRouter.Finlize failed: %w", err)
|
|
}
|
|
|
|
c.logger.Infof("%s.%s mesh provider %s reverted", r.Name, r.Namespace, provider)
|
|
return nil
|
|
}
|
|
|
|
// hasFinalizer evaluates the finalizers of a given canary for for existence of a provide finalizer string.
|
|
// It returns a boolean, true if the finalizer is found false otherwise.
|
|
func hasFinalizer(canary *flaggerv1.Canary) bool {
|
|
for _, f := range canary.ObjectMeta.Finalizers {
|
|
if f == finalizer {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// addFinalizer adds a provided finalizer to the specified canary resource.
|
|
// If failures occur the error will be returned otherwise the action is deemed successful
|
|
// and error will be nil.
|
|
func (c *Controller) addFinalizer(canary *flaggerv1.Canary) error {
|
|
firstTry := true
|
|
name, ns := canary.GetName(), canary.GetNamespace()
|
|
err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
|
if !firstTry {
|
|
canary, err = c.flaggerClient.FlaggerV1beta1().Canaries(ns).Get(context.TODO(), name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("canary %s.%s get query failed: %w", name, ns, err)
|
|
}
|
|
}
|
|
|
|
cCopy := canary.DeepCopy()
|
|
cCopy.ObjectMeta.Finalizers = append(cCopy.ObjectMeta.Finalizers, finalizer)
|
|
_, err = c.flaggerClient.FlaggerV1beta1().Canaries(canary.Namespace).Update(context.TODO(), cCopy, metav1.UpdateOptions{})
|
|
firstTry = false
|
|
return
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed after retries: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// removeFinalizer removes a provided finalizer to the specified canary resource.
|
|
// If failures occur the error will be returned otherwise the action is deemed successful
|
|
// and error will be nil.
|
|
func (c *Controller) removeFinalizer(canary *flaggerv1.Canary) error {
|
|
firstTry := true
|
|
name, ns := canary.GetName(), canary.GetNamespace()
|
|
err := retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
|
|
if !firstTry {
|
|
canary, err = c.flaggerClient.FlaggerV1beta1().Canaries(ns).Get(context.TODO(), name, metav1.GetOptions{})
|
|
if err != nil {
|
|
return fmt.Errorf("canary %s.%s get query failed: %w", name, ns, err)
|
|
}
|
|
}
|
|
|
|
cCopy := canary.DeepCopy()
|
|
|
|
nfs := make([]string, 0, len(cCopy.ObjectMeta.Finalizers))
|
|
for _, item := range cCopy.ObjectMeta.Finalizers {
|
|
if item != finalizer {
|
|
nfs = append(nfs, item)
|
|
}
|
|
}
|
|
|
|
if len(nfs) == 0 {
|
|
nfs = nil
|
|
}
|
|
|
|
cCopy.ObjectMeta.Finalizers = nfs
|
|
_, err = c.flaggerClient.FlaggerV1beta1().Canaries(canary.Namespace).Update(context.TODO(), cCopy, metav1.UpdateOptions{})
|
|
firstTry = false
|
|
return
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed after retries: %w", err)
|
|
}
|
|
return nil
|
|
}
|