vitess.io/vitess@v0.16.2/go/vt/topo/k8stopo/lock.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package k8stopo
    18  
    19  import (
    20  	"time"
    21  
    22  	"context"
    23  
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  
    27  	"vitess.io/vitess/go/vt/topo"
    28  	vtv1beta1 "vitess.io/vitess/go/vt/topo/k8stopo/apis/topo/v1beta1"
    29  )
    30  
    31  // kubernetesLockDescriptor implements topo.LockDescriptor.
    32  type kubernetesLockDescriptor struct {
    33  	s         *Server
    34  	leaseID   string
    35  	leasePath string
    36  }
    37  
    38  // Lock is part of the topo.Conn interface.
    39  func (s *Server) Lock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) {
    40  	return s.lock(ctx, dirPath, contents, false)
    41  }
    42  
    43  // TryLock is part of the topo.Conn interface. Its implementation is same as Lock
    44  func (s *Server) TryLock(ctx context.Context, dirPath, contents string) (topo.LockDescriptor, error) {
    45  	return s.Lock(ctx, dirPath, contents)
    46  }
    47  
    48  // lock is used by both Lock() and primary election.
    49  // it blocks until the lock is taken, interrupted, or times out
    50  func (s *Server) lock(ctx context.Context, nodePath, contents string, createMissing bool) (topo.LockDescriptor, error) {
    51  	// Satisfy the topo.Conn interface
    52  	if !createMissing {
    53  		// Per the topo.Conn interface:
    54  		// "Returns ErrNoNode if the directory doesn't exist (meaning
    55  		//   there is no existing file under that directory)."
    56  		if _, err := s.ListDir(ctx, nodePath, false); err != nil {
    57  			return nil, convertError(err, nodePath)
    58  		}
    59  	}
    60  
    61  	resource, err := s.buildFileResource(nodePath, []byte(contents))
    62  	if err != nil {
    63  		return nil, convertError(err, nodePath)
    64  	}
    65  
    66  	// mark locks as ephemeral
    67  	resource.Data.Ephemeral = true
    68  
    69  	var final *vtv1beta1.VitessTopoNode
    70  
    71  	for {
    72  		// Try and and create the resource. The kube api will handle the actual atomic lock creation
    73  		final, err = s.resourceClient.Create(ctx, resource, metav1.CreateOptions{})
    74  		if errors.IsAlreadyExists(err) {
    75  			select {
    76  			case <-time.After(10 * time.Millisecond):
    77  				continue // retry
    78  			case <-ctx.Done():
    79  				return nil, convertError(ctx.Err(), nodePath)
    80  			}
    81  		} else if err != nil {
    82  			return nil, convertError(err, nodePath)
    83  		}
    84  
    85  		break
    86  	}
    87  
    88  	// Update the internal cache
    89  	err = s.memberIndexer.Update(final)
    90  	if err != nil {
    91  		return nil, convertError(err, nodePath)
    92  	}
    93  
    94  	return &kubernetesLockDescriptor{
    95  		s:         s,
    96  		leaseID:   resource.Name,
    97  		leasePath: resource.Data.Key,
    98  	}, nil
    99  }
   100  
   101  // Check is part of the topo.LockDescriptor interface.
   102  func (ld *kubernetesLockDescriptor) Check(ctx context.Context) error {
   103  	// Get the object and ensure the leaseid
   104  	_, err := ld.s.resourceClient.Get(ctx, ld.leaseID, metav1.GetOptions{}) // TODO namespacing
   105  	if err != nil {
   106  		return convertError(err, ld.leasePath)
   107  
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // Unlock is part of the topo.LockDescriptor interface.
   114  func (ld *kubernetesLockDescriptor) Unlock(ctx context.Context) error {
   115  	err := ld.s.resourceClient.Delete(ctx, ld.leaseID, metav1.DeleteOptions{}) // TODO namespacing
   116  	if err != nil {
   117  		return convertError(err, ld.leasePath)
   118  	}
   119  	return nil
   120  }