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 }