...

Source file src/k8s.io/kubernetes/pkg/registry/core/service/portallocator/operation.go

Documentation: k8s.io/kubernetes/pkg/registry/core/service/portallocator

     1  /*
     2  Copyright 2015 The Kubernetes 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 portallocator
    18  
    19  // Encapsulates the semantics of a port allocation 'transaction':
    20  // It is better to leak ports than to double-allocate them,
    21  // so we allocate immediately, but defer release.
    22  // On commit we best-effort release the deferred releases.
    23  // On rollback we best-effort release any allocations we did.
    24  //
    25  // Pattern for use:
    26  //
    27  //	op := StartPortAllocationOperation(...)
    28  //	defer op.Finish
    29  //	...
    30  //	write(updatedOwner)
    31  //
    32  // /  op.Commit()
    33  type PortAllocationOperation struct {
    34  	pa              Interface
    35  	allocated       []int
    36  	releaseDeferred []int
    37  	shouldRollback  bool
    38  	dryRun          bool
    39  }
    40  
    41  // Creates a portAllocationOperation, tracking a set of allocations & releases
    42  // If dryRun is specified, never actually allocate or release anything
    43  func StartOperation(pa Interface, dryRun bool) *PortAllocationOperation {
    44  	op := &PortAllocationOperation{}
    45  	op.pa = pa
    46  	op.allocated = []int{}
    47  	op.releaseDeferred = []int{}
    48  	op.shouldRollback = true
    49  	op.dryRun = dryRun
    50  	return op
    51  }
    52  
    53  // Will rollback unless marked as shouldRollback = false by a Commit().  Call from a defer block
    54  func (op *PortAllocationOperation) Finish() {
    55  	if op.shouldRollback {
    56  		op.Rollback()
    57  	}
    58  }
    59  
    60  // (Try to) undo any operations we did
    61  func (op *PortAllocationOperation) Rollback() []error {
    62  	if op.dryRun {
    63  		return nil
    64  	}
    65  
    66  	errors := []error{}
    67  
    68  	for _, allocated := range op.allocated {
    69  		err := op.pa.Release(allocated)
    70  		if err != nil {
    71  			errors = append(errors, err)
    72  		}
    73  	}
    74  
    75  	if len(errors) == 0 {
    76  		return nil
    77  	}
    78  	return errors
    79  }
    80  
    81  // (Try to) perform any deferred operations.
    82  // Note that even if this fails, we don't rollback; we always want to err on the side of over-allocation,
    83  // and Commit should be called _after_ the owner is written
    84  func (op *PortAllocationOperation) Commit() []error {
    85  	if op.dryRun {
    86  		return nil
    87  	}
    88  
    89  	errors := []error{}
    90  
    91  	for _, release := range op.releaseDeferred {
    92  		err := op.pa.Release(release)
    93  		if err != nil {
    94  			errors = append(errors, err)
    95  		}
    96  	}
    97  
    98  	// Even on error, we don't rollback
    99  	// Problems should be fixed by an eventual reconciliation / restart
   100  	op.shouldRollback = false
   101  
   102  	if len(errors) == 0 {
   103  		return nil
   104  	}
   105  
   106  	return errors
   107  }
   108  
   109  // Allocates a port, and record it for future rollback
   110  func (op *PortAllocationOperation) Allocate(port int) error {
   111  	if op.dryRun {
   112  		if op.pa.Has(port) {
   113  			return ErrAllocated
   114  		}
   115  		for _, a := range op.allocated {
   116  			if port == a {
   117  				return ErrAllocated
   118  			}
   119  		}
   120  		op.allocated = append(op.allocated, port)
   121  		return nil
   122  	}
   123  
   124  	err := op.pa.Allocate(port)
   125  	if err == nil {
   126  		op.allocated = append(op.allocated, port)
   127  	}
   128  	return err
   129  }
   130  
   131  // Allocates a port, and record it for future rollback
   132  func (op *PortAllocationOperation) AllocateNext() (int, error) {
   133  	if op.dryRun {
   134  		// Find the max element of the allocated ports array.
   135  		// If no ports are already being allocated by this operation,
   136  		// then choose a sensible guess for a dummy port number
   137  		var lastPort int
   138  		for _, allocatedPort := range op.allocated {
   139  			if allocatedPort > lastPort {
   140  				lastPort = allocatedPort
   141  			}
   142  		}
   143  		if len(op.allocated) == 0 {
   144  			lastPort = 32768
   145  		}
   146  
   147  		// Try to find the next non allocated port.
   148  		// If too many ports are full, just reuse one,
   149  		// since this is just a dummy value.
   150  		for port := lastPort + 1; port < 100; port++ {
   151  			err := op.Allocate(port)
   152  			if err == nil {
   153  				return port, nil
   154  			}
   155  		}
   156  		op.allocated = append(op.allocated, lastPort+1)
   157  		return lastPort + 1, nil
   158  	}
   159  
   160  	port, err := op.pa.AllocateNext()
   161  	if err == nil {
   162  		op.allocated = append(op.allocated, port)
   163  	}
   164  	return port, err
   165  }
   166  
   167  // Marks a port so that it will be released if this operation Commits
   168  func (op *PortAllocationOperation) ReleaseDeferred(port int) {
   169  	op.releaseDeferred = append(op.releaseDeferred, port)
   170  }
   171  

View as plain text