From d7e41330409e18b8de5eefc189ee8171095a50bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roland=20Hu=C3=9F?= Date: Mon, 8 Jul 2019 10:00:45 +0200 Subject: [PATCH] fix(service update): Retry an update in case of a conflict. Is related to #224 and should fix one flake. --- pkg/kn/commands/service/service.go | 5 +++ pkg/kn/commands/service/service_create.go | 30 ++++++++++------ pkg/kn/commands/service/service_update.go | 43 ++++++++++++++--------- 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/pkg/kn/commands/service/service.go b/pkg/kn/commands/service/service.go index 7a4ff8ef2a..9f719a9399 100644 --- a/pkg/kn/commands/service/service.go +++ b/pkg/kn/commands/service/service.go @@ -19,6 +19,11 @@ import ( "github.com/spf13/cobra" ) +const ( + // How often to retry in case of an optimistic lock error when replacing a service (--force) + MaxUpdateRetries = 3 +) + func NewServiceCommand(p *commands.KnParams) *cobra.Command { serviceCmd := &cobra.Command{ Use: "service", diff --git a/pkg/kn/commands/service/service_create.go b/pkg/kn/commands/service/service_create.go index ba0f885031..1937c71a7e 100644 --- a/pkg/kn/commands/service/service_create.go +++ b/pkg/kn/commands/service/service_create.go @@ -131,17 +131,27 @@ func createService(client serving_v1alpha1_client.ServingV1alpha1Interface, serv } func replaceService(client serving_v1alpha1_client.ServingV1alpha1Interface, service *serving_v1alpha1_api.Service, namespace string, out io.Writer) error { - existingService, err := client.Services(namespace).Get(service.Name, v1.GetOptions{}) - if err != nil { - return err - } - service.ResourceVersion = existingService.ResourceVersion - _, err = client.Services(namespace).Update(service) - if err != nil { - return err + var retries = 0 + for true { + existingService, err := client.Services(namespace).Get(service.Name, v1.GetOptions{}) + if err != nil { + return err + } + service.ResourceVersion = existingService.ResourceVersion + _, err = client.Services(namespace).Update(service) + if err != nil { + // Retry to update when a resource version conflict exists + if api_errors.IsConflict(err) && retries < MaxUpdateRetries { + retries++ + continue + } + return err + } + fmt.Fprintf(out, "Service '%s' successfully replaced in namespace '%s'.\n", service.Name, namespace) + return nil } - fmt.Fprintf(out, "Service '%s' successfully replaced in namespace '%s'.\n", service.Name, namespace) - return nil + // This line will be never reached, but go insists on having it + return fmt.Errorf("can not update service '%s' in namespace '%s'", service.Name, namespace) } func serviceExists(client serving_v1alpha1_client.ServingV1alpha1Interface, name string, namespace string) (bool, error) { diff --git a/pkg/kn/commands/service/service_update.go b/pkg/kn/commands/service/service_update.go index e485bdb646..51ffe092a5 100644 --- a/pkg/kn/commands/service/service_update.go +++ b/pkg/kn/commands/service/service_update.go @@ -18,9 +18,11 @@ import ( "errors" "fmt" - "github.com/knative/client/pkg/kn/commands" "github.com/spf13/cobra" + api_errors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/knative/client/pkg/kn/commands" ) func NewServiceUpdateCommand(p *commands.KnParams) *cobra.Command { @@ -53,24 +55,33 @@ func NewServiceUpdateCommand(p *commands.KnParams) *cobra.Command { return err } - service, err := client.Services(namespace).Get(args[0], v1.GetOptions{}) - if err != nil { - return err - } - service = service.DeepCopy() + var retries = 0 + for true { + service, err := client.Services(namespace).Get(args[0], v1.GetOptions{}) + if err != nil { + return err + } + service = service.DeepCopy() - err = editFlags.Apply(service, cmd) - if err != nil { - return err - } + err = editFlags.Apply(service, cmd) + if err != nil { + return err + } - _, err = client.Services(namespace).Update(service) - if err != nil { - return err + _, err = client.Services(namespace).Update(service) + if err != nil { + // Retry to update when a resource version conflict exists + if api_errors.IsConflict(err) && retries < MaxUpdateRetries { + retries++ + continue + } + return err + } + fmt.Fprintf(cmd.OutOrStdout(), "Service '%s' updated in namespace '%s'.\n", args[0], namespace) + return nil } - - fmt.Fprintf(cmd.OutOrStdout(), "Service '%s' updated in namespace '%s'.\n", args[0], namespace) - return nil + // This line will be never reached, but go insists on having it + return fmt.Errorf("can not update service '%s' in namespace '%s'", args[0], namespace) }, }