...

Source file src/k8s.io/kubernetes/test/integration/ipamperf/cloud.go

Documentation: k8s.io/kubernetes/test/integration/ipamperf

     1  //go:build !providerless
     2  // +build !providerless
     3  
     4  /*
     5  Copyright 2018 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package ipamperf
    21  
    22  import (
    23  	"context"
    24  	"net"
    25  	"sync"
    26  
    27  	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud"
    28  	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
    29  	beta "google.golang.org/api/compute/v0.beta"
    30  	ga "google.golang.org/api/compute/v1"
    31  	"k8s.io/kubernetes/pkg/controller/nodeipam/ipam/cidrset"
    32  	"k8s.io/kubernetes/test/integration/util"
    33  )
    34  
    35  // implemntation note:
    36  // ------------------
    37  // cloud.go implements hooks and handler functions for the MockGCE cloud in order to meet expectations
    38  // of cloud behavior from the IPAM controllers. The key constraint is that the IPAM code is spread
    39  // across both GA and Beta instances, which are distinct objects in the mock. We need to solve for
    40  //
    41  // 1. When a GET is called on an instance, we lazy create the instance with or without an assigned
    42  //    ip alias as needed by the IPAM controller type
    43  // 2. When we assign an IP alias for an instance, both the GA and Beta instance have to agree on the
    44  //    assigned alias range
    45  //
    46  // We solve both the problems by using a baseInstanceList which maintains a list of known instances,
    47  // and their pre-assigned ip-alias ranges (if needed). We then create GetHook for GA and Beta GetInstance
    48  // calls as closures over this betaInstanceList that can lookup base instance data.
    49  //
    50  // This has the advantage that once the Get hook pouplates the GCEMock with the base data, we then let the
    51  // rest of the mock code run as is.
    52  
    53  // baseInstance tracks basic instance data needed by the IPAM controllers
    54  type baseInstance struct {
    55  	name       string
    56  	zone       string
    57  	aliasRange string
    58  }
    59  
    60  // baseInstanceList tracks a set of base instances
    61  type baseInstanceList struct {
    62  	allocateCIDR   bool
    63  	clusterCIDR    *net.IPNet
    64  	subnetMaskSize int
    65  	cidrSet        *cidrset.CidrSet
    66  
    67  	lock      sync.Mutex // protect access to instances
    68  	instances map[meta.Key]*baseInstance
    69  }
    70  
    71  // toGA is an utility method to return the baseInstance data as a GA Instance object
    72  func (bi *baseInstance) toGA() *ga.Instance {
    73  	inst := &ga.Instance{Name: bi.name, Zone: bi.zone, NetworkInterfaces: []*ga.NetworkInterface{{}}}
    74  	if bi.aliasRange != "" {
    75  		inst.NetworkInterfaces[0].AliasIpRanges = []*ga.AliasIpRange{
    76  			{IpCidrRange: bi.aliasRange, SubnetworkRangeName: util.TestSecondaryRangeName},
    77  		}
    78  	}
    79  	return inst
    80  }
    81  
    82  // toGA is an utility method to return the baseInstance data as a beta Instance object
    83  func (bi *baseInstance) toBeta() *beta.Instance {
    84  	inst := &beta.Instance{Name: bi.name, Zone: bi.zone, NetworkInterfaces: []*beta.NetworkInterface{{}}}
    85  	if bi.aliasRange != "" {
    86  		inst.NetworkInterfaces[0].AliasIpRanges = []*beta.AliasIpRange{
    87  			{IpCidrRange: bi.aliasRange, SubnetworkRangeName: util.TestSecondaryRangeName},
    88  		}
    89  	}
    90  	return inst
    91  }
    92  
    93  // newBaseInstanceList is the baseInstanceList constructor
    94  func newBaseInstanceList(allocateCIDR bool, clusterCIDR *net.IPNet, subnetMaskSize int) *baseInstanceList {
    95  	cidrSet, _ := cidrset.NewCIDRSet(clusterCIDR, subnetMaskSize)
    96  	return &baseInstanceList{
    97  		allocateCIDR:   allocateCIDR,
    98  		clusterCIDR:    clusterCIDR,
    99  		subnetMaskSize: subnetMaskSize,
   100  		cidrSet:        cidrSet,
   101  		instances:      make(map[meta.Key]*baseInstance),
   102  	}
   103  }
   104  
   105  // getOrCreateBaseInstance lazily creates a new base instance, assigning if allocateCIDR is true
   106  func (bil *baseInstanceList) getOrCreateBaseInstance(key *meta.Key) *baseInstance {
   107  	bil.lock.Lock()
   108  	defer bil.lock.Unlock()
   109  
   110  	inst, found := bil.instances[*key]
   111  	if !found {
   112  		inst = &baseInstance{name: key.Name, zone: key.Zone}
   113  		if bil.allocateCIDR {
   114  			nextRange, _ := bil.cidrSet.AllocateNext()
   115  			inst.aliasRange = nextRange.String()
   116  		}
   117  		bil.instances[*key] = inst
   118  	}
   119  	return inst
   120  }
   121  
   122  // newGAGetHook creates a new closure with the current baseInstanceList to be used as a MockInstances.GetHook
   123  func (bil *baseInstanceList) newGAGetHook() func(ctx context.Context, key *meta.Key, m *cloud.MockInstances) (bool, *ga.Instance, error) {
   124  	return func(ctx context.Context, key *meta.Key, m *cloud.MockInstances) (bool, *ga.Instance, error) {
   125  		m.Lock.Lock()
   126  		defer m.Lock.Unlock()
   127  
   128  		if _, found := m.Objects[*key]; !found {
   129  			m.Objects[*key] = &cloud.MockInstancesObj{Obj: bil.getOrCreateBaseInstance(key).toGA()}
   130  		}
   131  		return false, nil, nil
   132  	}
   133  }
   134  
   135  // newBetaGetHook creates a new closure with the current baseInstanceList to be used as a MockBetaInstances.GetHook
   136  func (bil *baseInstanceList) newBetaGetHook() func(ctx context.Context, key *meta.Key, m *cloud.MockBetaInstances) (bool, *beta.Instance, error) {
   137  	return func(ctx context.Context, key *meta.Key, m *cloud.MockBetaInstances) (bool, *beta.Instance, error) {
   138  		m.Lock.Lock()
   139  		defer m.Lock.Unlock()
   140  
   141  		if _, found := m.Objects[*key]; !found {
   142  			m.Objects[*key] = &cloud.MockInstancesObj{Obj: bil.getOrCreateBaseInstance(key).toBeta()}
   143  		}
   144  		return false, nil, nil
   145  	}
   146  }
   147  
   148  // newMockCloud returns a mock GCE instance with the appropriate handlers hooks
   149  func (bil *baseInstanceList) newMockCloud() cloud.Cloud {
   150  	c := cloud.NewMockGCE(nil)
   151  
   152  	// insert hooks to lazy create a instance when needed
   153  	c.MockInstances.GetHook = bil.newGAGetHook()
   154  	c.MockBetaInstances.GetHook = bil.newBetaGetHook()
   155  
   156  	return c
   157  }
   158  

View as plain text