k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/core/service/storage/storage.go (about)

     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 storage
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math/rand"
    23  	"net"
    24  	"net/http"
    25  	"net/url"
    26  	"strconv"
    27  
    28  	"k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	utilnet "k8s.io/apimachinery/pkg/util/net"
    32  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    33  	"k8s.io/apimachinery/pkg/util/sets"
    34  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    35  	"k8s.io/apiserver/pkg/registry/generic"
    36  	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
    37  	"k8s.io/apiserver/pkg/registry/rest"
    38  	"k8s.io/apiserver/pkg/util/dryrun"
    39  	"k8s.io/klog/v2"
    40  	api "k8s.io/kubernetes/pkg/apis/core"
    41  	"k8s.io/kubernetes/pkg/printers"
    42  	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
    43  	printerstorage "k8s.io/kubernetes/pkg/printers/storage"
    44  	svcreg "k8s.io/kubernetes/pkg/registry/core/service"
    45  	"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
    46  	"k8s.io/kubernetes/pkg/registry/core/service/portallocator"
    47  	netutil "k8s.io/utils/net"
    48  	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
    49  )
    50  
    51  type EndpointsStorage interface {
    52  	rest.Getter
    53  	rest.GracefulDeleter
    54  }
    55  
    56  type PodStorage interface {
    57  	rest.Getter
    58  }
    59  
    60  type REST struct {
    61  	*genericregistry.Store
    62  	primaryIPFamily   api.IPFamily
    63  	secondaryIPFamily api.IPFamily
    64  	alloc             Allocators
    65  	endpoints         EndpointsStorage
    66  	pods              PodStorage
    67  	proxyTransport    http.RoundTripper
    68  }
    69  
    70  var (
    71  	_ rest.CategoriesProvider     = &REST{}
    72  	_ rest.ShortNamesProvider     = &REST{}
    73  	_ rest.StorageVersionProvider = &REST{}
    74  	_ rest.ResetFieldsStrategy    = &REST{}
    75  	_ rest.Redirector             = &REST{}
    76  )
    77  
    78  // NewREST returns a REST object that will work against services.
    79  func NewREST(
    80  	optsGetter generic.RESTOptionsGetter,
    81  	serviceIPFamily api.IPFamily,
    82  	ipAllocs map[api.IPFamily]ipallocator.Interface,
    83  	portAlloc portallocator.Interface,
    84  	endpoints EndpointsStorage,
    85  	pods PodStorage,
    86  	proxyTransport http.RoundTripper) (*REST, *StatusREST, *svcreg.ProxyREST, error) {
    87  
    88  	store := &genericregistry.Store{
    89  		NewFunc:                   func() runtime.Object { return &api.Service{} },
    90  		NewListFunc:               func() runtime.Object { return &api.ServiceList{} },
    91  		PredicateFunc:             svcreg.Matcher,
    92  		DefaultQualifiedResource:  api.Resource("services"),
    93  		SingularQualifiedResource: api.Resource("service"),
    94  		ReturnDeletedObject:       true,
    95  
    96  		CreateStrategy:      svcreg.Strategy,
    97  		UpdateStrategy:      svcreg.Strategy,
    98  		DeleteStrategy:      svcreg.Strategy,
    99  		ResetFieldsStrategy: svcreg.Strategy,
   100  
   101  		TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
   102  	}
   103  	options := &generic.StoreOptions{
   104  		RESTOptions: optsGetter,
   105  		AttrFunc:    svcreg.GetAttrs,
   106  	}
   107  	if err := store.CompleteWithOptions(options); err != nil {
   108  		return nil, nil, nil, err
   109  	}
   110  
   111  	statusStore := *store
   112  	statusStore.UpdateStrategy = svcreg.StatusStrategy
   113  	statusStore.ResetFieldsStrategy = svcreg.StatusStrategy
   114  
   115  	var primaryIPFamily api.IPFamily = serviceIPFamily
   116  	var secondaryIPFamily api.IPFamily = "" // sentinel value
   117  	if len(ipAllocs) > 1 {
   118  		secondaryIPFamily = otherFamily(serviceIPFamily)
   119  	}
   120  	genericStore := &REST{
   121  		Store:             store,
   122  		primaryIPFamily:   primaryIPFamily,
   123  		secondaryIPFamily: secondaryIPFamily,
   124  		alloc:             makeAlloc(serviceIPFamily, ipAllocs, portAlloc),
   125  		endpoints:         endpoints,
   126  		pods:              pods,
   127  		proxyTransport:    proxyTransport,
   128  	}
   129  	store.Decorator = genericStore.defaultOnRead
   130  	store.AfterDelete = genericStore.afterDelete
   131  	store.BeginCreate = genericStore.beginCreate
   132  	store.BeginUpdate = genericStore.beginUpdate
   133  
   134  	// users can patch the status to remove the finalizer,
   135  	// hence statusStore must participate on the AfterDelete
   136  	// hook to release the allocated resources
   137  	statusStore.AfterDelete = genericStore.afterDelete
   138  
   139  	return genericStore, &StatusREST{store: &statusStore}, &svcreg.ProxyREST{Redirector: genericStore, ProxyTransport: proxyTransport}, nil
   140  }
   141  
   142  // otherFamily returns the non-selected IPFamily.  This assumes the input is
   143  // valid.
   144  func otherFamily(fam api.IPFamily) api.IPFamily {
   145  	if fam == api.IPv4Protocol {
   146  		return api.IPv6Protocol
   147  	}
   148  	return api.IPv4Protocol
   149  }
   150  
   151  var (
   152  	_ rest.ShortNamesProvider = &REST{}
   153  	_ rest.CategoriesProvider = &REST{}
   154  )
   155  
   156  // ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
   157  func (r *REST) ShortNames() []string {
   158  	return []string{"svc"}
   159  }
   160  
   161  // Categories implements the CategoriesProvider interface. Returns a list of categories a resource is part of.
   162  func (r *REST) Categories() []string {
   163  	return []string{"all"}
   164  }
   165  
   166  // Destroy cleans up everything on shutdown.
   167  func (r *REST) Destroy() {
   168  	r.Store.Destroy()
   169  	r.alloc.Destroy()
   170  }
   171  
   172  // StatusREST implements the REST endpoint for changing the status of a service.
   173  type StatusREST struct {
   174  	store *genericregistry.Store
   175  }
   176  
   177  func (r *StatusREST) New() runtime.Object {
   178  	return &api.Service{}
   179  }
   180  
   181  // Destroy cleans up resources on shutdown.
   182  func (r *StatusREST) Destroy() {
   183  	// Given that underlying store is shared with REST,
   184  	// we don't destroy it here explicitly.
   185  }
   186  
   187  // Get retrieves the object from the storage. It is required to support Patch.
   188  func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
   189  	return r.store.Get(ctx, name, options)
   190  }
   191  
   192  // Update alters the status subset of an object.
   193  func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
   194  	// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
   195  	// subresources should never allow create on update.
   196  	return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
   197  }
   198  
   199  func (r *StatusREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
   200  	return r.store.ConvertToTable(ctx, object, tableOptions)
   201  }
   202  
   203  // GetResetFields implements rest.ResetFieldsStrategy
   204  func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
   205  	return r.store.GetResetFields()
   206  }
   207  
   208  // We have a lot of functions that take a pair of "before" and "after" or
   209  // "oldSvc" and "newSvc" args.  Convention across the codebase is to pass them
   210  // as (new, old), but it's easy to screw up when they are the same type.
   211  //
   212  // These types force us to pay attention.  If the order of the arguments
   213  // matters, please receive them as:
   214  //    func something(after After, before Before) {
   215  //        oldSvc, newSvc := before.Service, after.Service
   216  //
   217  // If the order of arguments DOES NOT matter, please receive them as:
   218  //    func something(lhs, rhs *api.Service) {
   219  
   220  type Before struct {
   221  	*api.Service
   222  }
   223  type After struct {
   224  	*api.Service
   225  }
   226  
   227  // defaultOnRead sets interlinked fields that were not previously set on read.
   228  // We can't do this in the normal defaulting path because that same logic
   229  // applies on Get, Create, and Update, but we need to distinguish between them.
   230  //
   231  // This will be called on both Service and ServiceList types.
   232  func (r *REST) defaultOnRead(obj runtime.Object) {
   233  	switch s := obj.(type) {
   234  	case *api.Service:
   235  		r.defaultOnReadService(s)
   236  	case *api.ServiceList:
   237  		r.defaultOnReadServiceList(s)
   238  	default:
   239  		// This was not an object we can default.  This is not an error, as the
   240  		// caching layer can pass through here, too.
   241  	}
   242  }
   243  
   244  // defaultOnReadServiceList defaults a ServiceList.
   245  func (r *REST) defaultOnReadServiceList(serviceList *api.ServiceList) {
   246  	if serviceList == nil {
   247  		return
   248  	}
   249  
   250  	for i := range serviceList.Items {
   251  		r.defaultOnReadService(&serviceList.Items[i])
   252  	}
   253  }
   254  
   255  // defaultOnReadService defaults a single Service.
   256  func (r *REST) defaultOnReadService(service *api.Service) {
   257  	if service == nil {
   258  		return
   259  	}
   260  
   261  	// We might find Services that were written before ClusterIP became plural.
   262  	// We still want to present a consistent view of them.
   263  	normalizeClusterIPs(After{service}, Before{nil})
   264  
   265  	// Set ipFamilies and ipFamilyPolicy if needed.
   266  	r.defaultOnReadIPFamilies(service)
   267  
   268  	// We unintentionally defaulted internalTrafficPolicy when it's not needed
   269  	// for the ExternalName type. It's too late to change the field in storage,
   270  	// but we can drop the field when read.
   271  	defaultOnReadInternalTrafficPolicy(service)
   272  }
   273  
   274  func defaultOnReadInternalTrafficPolicy(service *api.Service) {
   275  	if service.Spec.Type == api.ServiceTypeExternalName {
   276  		service.Spec.InternalTrafficPolicy = nil
   277  	}
   278  }
   279  
   280  func (r *REST) defaultOnReadIPFamilies(service *api.Service) {
   281  	// ExternalName does not need this.
   282  	if !needsClusterIP(service) {
   283  		return
   284  	}
   285  
   286  	// If IPFamilies is set, we assume IPFamilyPolicy is also set (it should
   287  	// not be possible to have one and not the other), and therefore we don't
   288  	// need further defaulting.  Likewise, if IPFamilies is *not* set, we
   289  	// assume IPFamilyPolicy can't be set either.
   290  	if len(service.Spec.IPFamilies) > 0 {
   291  		return
   292  	}
   293  
   294  	singleStack := api.IPFamilyPolicySingleStack
   295  	requireDualStack := api.IPFamilyPolicyRequireDualStack
   296  
   297  	if service.Spec.ClusterIP == api.ClusterIPNone {
   298  		// Headless.
   299  		if len(service.Spec.Selector) == 0 {
   300  			// Headless + selectorless is a special-case.
   301  			//
   302  			// At this stage we don't know what kind of endpoints (specifically
   303  			// their IPFamilies) the user has assigned to this selectorless
   304  			// service. We assume it has dual-stack and we default it to
   305  			// RequireDualStack on any cluster (single- or dual-stack
   306  			// configured).
   307  			service.Spec.IPFamilyPolicy = &requireDualStack
   308  			service.Spec.IPFamilies = []api.IPFamily{r.primaryIPFamily, otherFamily(r.primaryIPFamily)}
   309  		} else {
   310  			// Headless + selector - default to single.
   311  			service.Spec.IPFamilyPolicy = &singleStack
   312  			service.Spec.IPFamilies = []api.IPFamily{r.primaryIPFamily}
   313  		}
   314  	} else {
   315  		// Headful: init ipFamilies from clusterIPs.
   316  		service.Spec.IPFamilies = make([]api.IPFamily, len(service.Spec.ClusterIPs))
   317  		for idx, ip := range service.Spec.ClusterIPs {
   318  			if netutil.IsIPv6String(ip) {
   319  				service.Spec.IPFamilies[idx] = api.IPv6Protocol
   320  			} else {
   321  				service.Spec.IPFamilies[idx] = api.IPv4Protocol
   322  			}
   323  		}
   324  		if len(service.Spec.IPFamilies) == 1 {
   325  			service.Spec.IPFamilyPolicy = &singleStack
   326  		} else if len(service.Spec.IPFamilies) == 2 {
   327  			// It shouldn't be possible to get here, but just in case.
   328  			service.Spec.IPFamilyPolicy = &requireDualStack
   329  		}
   330  	}
   331  }
   332  
   333  func (r *REST) afterDelete(obj runtime.Object, options *metav1.DeleteOptions) {
   334  	svc := obj.(*api.Service)
   335  
   336  	// Normally this defaulting is done automatically, but the hook (Decorator)
   337  	// is called at the end of this process, and we want the fully-formed
   338  	// object.
   339  	r.defaultOnReadService(svc)
   340  
   341  	// Only perform the cleanup if this is a non-dryrun deletion
   342  	if !dryrun.IsDryRun(options.DryRun) {
   343  		// It would be better if we had the caller context, but that changes
   344  		// this hook signature.
   345  		ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), svc.Namespace)
   346  		// TODO: This is clumsy.  It was added for fear that the endpoints
   347  		// controller might lag, and we could end up rusing the service name
   348  		// with old endpoints.  We should solve that better and remove this, or
   349  		// else we should do this for EndpointSlice, too.
   350  		_, _, err := r.endpoints.Delete(ctx, svc.Name, rest.ValidateAllObjectFunc, &metav1.DeleteOptions{})
   351  		if err != nil && !errors.IsNotFound(err) {
   352  			klog.Errorf("delete service endpoints %s/%s failed: %v", svc.Name, svc.Namespace, err)
   353  		}
   354  
   355  		r.alloc.releaseAllocatedResources(svc)
   356  	}
   357  }
   358  
   359  func (r *REST) beginCreate(ctx context.Context, obj runtime.Object, options *metav1.CreateOptions) (genericregistry.FinishFunc, error) {
   360  	svc := obj.(*api.Service)
   361  
   362  	// Make sure ClusterIP and ClusterIPs are in sync.  This has to happen
   363  	// early, before anyone looks at them.
   364  	normalizeClusterIPs(After{svc}, Before{nil})
   365  
   366  	// Allocate IPs and ports. If we had a transactional store, this would just
   367  	// be part of the larger transaction.  We don't have that, so we have to do
   368  	// it manually. This has to happen here and not in any earlier hooks (e.g.
   369  	// defaulting) because it needs to be aware of flags and be able to access
   370  	// API storage.
   371  	txn, err := r.alloc.allocateCreate(svc, dryrun.IsDryRun(options.DryRun))
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	// Our cleanup callback
   377  	finish := func(_ context.Context, success bool) {
   378  		if success {
   379  			txn.Commit()
   380  		} else {
   381  			txn.Revert()
   382  		}
   383  	}
   384  
   385  	return finish, nil
   386  }
   387  
   388  func (r *REST) beginUpdate(ctx context.Context, obj, oldObj runtime.Object, options *metav1.UpdateOptions) (genericregistry.FinishFunc, error) {
   389  	newSvc := obj.(*api.Service)
   390  	oldSvc := oldObj.(*api.Service)
   391  
   392  	// Make sure the existing object has all fields we expect to be defaulted.
   393  	// This might not be true if the saved object predates these fields (the
   394  	// Decorator hook is not called on 'old' in the update path.
   395  	r.defaultOnReadService(oldSvc)
   396  
   397  	// Fix up allocated values that the client may have not specified (for
   398  	// idempotence).
   399  	patchAllocatedValues(After{newSvc}, Before{oldSvc})
   400  
   401  	// Make sure ClusterIP and ClusterIPs are in sync.  This has to happen
   402  	// early, before anyone looks at them.
   403  	normalizeClusterIPs(After{newSvc}, Before{oldSvc})
   404  
   405  	// Allocate and initialize fields.
   406  	txn, err := r.alloc.allocateUpdate(After{newSvc}, Before{oldSvc}, dryrun.IsDryRun(options.DryRun))
   407  	if err != nil {
   408  		return nil, err
   409  	}
   410  
   411  	// Our cleanup callback
   412  	finish := func(_ context.Context, success bool) {
   413  		if success {
   414  			txn.Commit()
   415  		} else {
   416  			txn.Revert()
   417  		}
   418  	}
   419  
   420  	return finish, nil
   421  }
   422  
   423  // ResourceLocation returns a URL to which one can send traffic for the specified service.
   424  func (r *REST) ResourceLocation(ctx context.Context, id string) (*url.URL, http.RoundTripper, error) {
   425  	// Allow ID as "svcname", "svcname:port", or "scheme:svcname:port".
   426  	svcScheme, svcName, portStr, valid := utilnet.SplitSchemeNamePort(id)
   427  	if !valid {
   428  		return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid service request %q", id))
   429  	}
   430  
   431  	// If a port *number* was specified, find the corresponding service port name
   432  	if portNum, err := strconv.ParseInt(portStr, 10, 64); err == nil {
   433  		obj, err := r.Get(ctx, svcName, &metav1.GetOptions{})
   434  		if err != nil {
   435  			return nil, nil, err
   436  		}
   437  		svc := obj.(*api.Service)
   438  		found := false
   439  		for _, svcPort := range svc.Spec.Ports {
   440  			if int64(svcPort.Port) == portNum {
   441  				// use the declared port's name
   442  				portStr = svcPort.Name
   443  				found = true
   444  				break
   445  			}
   446  		}
   447  		if !found {
   448  			return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no service port %d found for service %q", portNum, svcName))
   449  		}
   450  	}
   451  
   452  	obj, err := r.endpoints.Get(ctx, svcName, &metav1.GetOptions{})
   453  	if err != nil {
   454  		return nil, nil, err
   455  	}
   456  	eps := obj.(*api.Endpoints)
   457  	if len(eps.Subsets) == 0 {
   458  		return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", svcName))
   459  	}
   460  	// Pick a random Subset to start searching from.
   461  	ssSeed := rand.Intn(len(eps.Subsets))
   462  	// Find a Subset that has the port.
   463  	for ssi := 0; ssi < len(eps.Subsets); ssi++ {
   464  		ss := &eps.Subsets[(ssSeed+ssi)%len(eps.Subsets)]
   465  		if len(ss.Addresses) == 0 {
   466  			continue
   467  		}
   468  		for i := range ss.Ports {
   469  			if ss.Ports[i].Name == portStr {
   470  				addrSeed := rand.Intn(len(ss.Addresses))
   471  				// This is a little wonky, but it's expensive to test for the presence of a Pod
   472  				// So we repeatedly try at random and validate it, this means that for an invalid
   473  				// service with a lot of endpoints we're going to potentially make a lot of calls,
   474  				// but in the expected case we'll only make one.
   475  				for try := 0; try < len(ss.Addresses); try++ {
   476  					addr := ss.Addresses[(addrSeed+try)%len(ss.Addresses)]
   477  					// We only proxy to addresses that are actually pods.
   478  					if err := isValidAddress(ctx, &addr, r.pods); err != nil {
   479  						utilruntime.HandleError(fmt.Errorf("Address %v isn't valid (%v)", addr, err))
   480  						continue
   481  					}
   482  					ip := addr.IP
   483  					port := int(ss.Ports[i].Port)
   484  					return &url.URL{
   485  						Scheme: svcScheme,
   486  						Host:   net.JoinHostPort(ip, strconv.Itoa(port)),
   487  					}, r.proxyTransport, nil
   488  				}
   489  				utilruntime.HandleError(fmt.Errorf("Failed to find a valid address, skipping subset: %v", ss))
   490  			}
   491  		}
   492  	}
   493  	return nil, nil, errors.NewServiceUnavailable(fmt.Sprintf("no endpoints available for service %q", id))
   494  }
   495  
   496  func isValidAddress(ctx context.Context, addr *api.EndpointAddress, pods rest.Getter) error {
   497  	if addr.TargetRef == nil {
   498  		return fmt.Errorf("Address has no target ref, skipping: %v", addr)
   499  	}
   500  	if genericapirequest.NamespaceValue(ctx) != addr.TargetRef.Namespace {
   501  		return fmt.Errorf("Address namespace doesn't match context namespace")
   502  	}
   503  	obj, err := pods.Get(ctx, addr.TargetRef.Name, &metav1.GetOptions{})
   504  	if err != nil {
   505  		return err
   506  	}
   507  	pod, ok := obj.(*api.Pod)
   508  	if !ok {
   509  		return fmt.Errorf("failed to cast to pod: %v", obj)
   510  	}
   511  	if pod == nil {
   512  		return fmt.Errorf("pod is missing, skipping (%s/%s)", addr.TargetRef.Namespace, addr.TargetRef.Name)
   513  	}
   514  	for _, podIP := range pod.Status.PodIPs {
   515  		if podIP.IP == addr.IP {
   516  			return nil
   517  		}
   518  	}
   519  	return fmt.Errorf("pod ip(s) doesn't match endpoint ip, skipping: %v vs %s (%s/%s)", pod.Status.PodIPs, addr.IP, addr.TargetRef.Namespace, addr.TargetRef.Name)
   520  }
   521  
   522  // normalizeClusterIPs adjust clusterIPs based on ClusterIP.  This must not
   523  // consider any other fields.
   524  func normalizeClusterIPs(after After, before Before) {
   525  	oldSvc, newSvc := before.Service, after.Service
   526  
   527  	// In all cases here, we don't need to over-think the inputs.  Validation
   528  	// will be called on the new object soon enough.  All this needs to do is
   529  	// try to divine what user meant with these linked fields. The below
   530  	// is verbosely written for clarity.
   531  
   532  	// **** IMPORTANT *****
   533  	// as a governing rule. User must (either)
   534  	// -- Use singular only (old client)
   535  	// -- singular and plural fields (new clients)
   536  
   537  	if oldSvc == nil {
   538  		// This was a create operation.
   539  		// User specified singular and not plural (e.g. an old client), so init
   540  		// plural for them.
   541  		if len(newSvc.Spec.ClusterIP) > 0 && len(newSvc.Spec.ClusterIPs) == 0 {
   542  			newSvc.Spec.ClusterIPs = []string{newSvc.Spec.ClusterIP}
   543  			return
   544  		}
   545  
   546  		// we don't init singular based on plural because
   547  		// new client must use both fields
   548  
   549  		// Either both were not specified (will be allocated) or both were
   550  		// specified (will be validated).
   551  		return
   552  	}
   553  
   554  	// This was an update operation
   555  
   556  	// ClusterIPs were cleared by an old client which was trying to patch
   557  	// some field and didn't provide ClusterIPs
   558  	if len(oldSvc.Spec.ClusterIPs) > 0 && len(newSvc.Spec.ClusterIPs) == 0 {
   559  		// if ClusterIP is the same, then it is an old client trying to
   560  		// patch service and didn't provide ClusterIPs
   561  		if oldSvc.Spec.ClusterIP == newSvc.Spec.ClusterIP {
   562  			newSvc.Spec.ClusterIPs = oldSvc.Spec.ClusterIPs
   563  		}
   564  	}
   565  
   566  	// clusterIP is not the same
   567  	if oldSvc.Spec.ClusterIP != newSvc.Spec.ClusterIP {
   568  		// this is a client trying to clear it
   569  		if len(oldSvc.Spec.ClusterIP) > 0 && len(newSvc.Spec.ClusterIP) == 0 {
   570  			// if clusterIPs are the same, then clear on their behalf
   571  			if sameClusterIPs(oldSvc, newSvc) {
   572  				newSvc.Spec.ClusterIPs = nil
   573  			}
   574  
   575  			// if they provided nil, then we are fine (handled by patching case above)
   576  			// if they changed it then validation will catch it
   577  		} else {
   578  			// ClusterIP has changed but not cleared *and* ClusterIPs are the same
   579  			// then we set ClusterIPs based on ClusterIP
   580  			if sameClusterIPs(oldSvc, newSvc) {
   581  				newSvc.Spec.ClusterIPs = []string{newSvc.Spec.ClusterIP}
   582  			}
   583  		}
   584  	}
   585  }
   586  
   587  // patchAllocatedValues allows clients to avoid a read-modify-write cycle while
   588  // preserving values that we allocated on their behalf.  For example, they
   589  // might create a Service without specifying the ClusterIP, in which case we
   590  // allocate one.  If they resubmit that same YAML, we want it to succeed.
   591  func patchAllocatedValues(after After, before Before) {
   592  	oldSvc, newSvc := before.Service, after.Service
   593  
   594  	if needsClusterIP(oldSvc) && needsClusterIP(newSvc) {
   595  		if newSvc.Spec.ClusterIP == "" {
   596  			newSvc.Spec.ClusterIP = oldSvc.Spec.ClusterIP
   597  		}
   598  		if len(newSvc.Spec.ClusterIPs) == 0 && len(oldSvc.Spec.ClusterIPs) > 0 {
   599  			newSvc.Spec.ClusterIPs = oldSvc.Spec.ClusterIPs
   600  		}
   601  	}
   602  
   603  	if needsNodePort(oldSvc) && needsNodePort(newSvc) {
   604  		nodePortsUsed := func(svc *api.Service) sets.Int32 {
   605  			used := sets.NewInt32()
   606  			for _, p := range svc.Spec.Ports {
   607  				if p.NodePort != 0 {
   608  					used.Insert(p.NodePort)
   609  				}
   610  			}
   611  			return used
   612  		}
   613  
   614  		// Build a set of all the ports in oldSvc that are also in newSvc.  We know
   615  		// we can't patch these values.
   616  		used := nodePortsUsed(oldSvc).Intersection(nodePortsUsed(newSvc))
   617  
   618  		// Map NodePorts by name.  The user may have changed other properties
   619  		// of the port, but we won't see that here.
   620  		np := map[string]int32{}
   621  		for i := range oldSvc.Spec.Ports {
   622  			p := &oldSvc.Spec.Ports[i]
   623  			np[p.Name] = p.NodePort
   624  		}
   625  
   626  		// If newSvc is missing values, try to patch them in when we know them and
   627  		// they haven't been used for another port.
   628  
   629  		for i := range newSvc.Spec.Ports {
   630  			p := &newSvc.Spec.Ports[i]
   631  			if p.NodePort == 0 {
   632  				oldVal := np[p.Name]
   633  				if !used.Has(oldVal) {
   634  					p.NodePort = oldVal
   635  				}
   636  			}
   637  		}
   638  	}
   639  
   640  	if needsHCNodePort(oldSvc) && needsHCNodePort(newSvc) {
   641  		if newSvc.Spec.HealthCheckNodePort == 0 {
   642  			newSvc.Spec.HealthCheckNodePort = oldSvc.Spec.HealthCheckNodePort
   643  		}
   644  	}
   645  }
   646  
   647  func needsClusterIP(svc *api.Service) bool {
   648  	if svc.Spec.Type == api.ServiceTypeExternalName {
   649  		return false
   650  	}
   651  	return true
   652  }
   653  
   654  func needsNodePort(svc *api.Service) bool {
   655  	if svc.Spec.Type == api.ServiceTypeNodePort {
   656  		return true
   657  	}
   658  	if svc.Spec.Type == api.ServiceTypeLoadBalancer &&
   659  		(svc.Spec.AllocateLoadBalancerNodePorts == nil || *svc.Spec.AllocateLoadBalancerNodePorts) {
   660  		return true
   661  	}
   662  	return false
   663  }
   664  
   665  func needsHCNodePort(svc *api.Service) bool {
   666  	if svc.Spec.Type != api.ServiceTypeLoadBalancer {
   667  		return false
   668  	}
   669  	if svc.Spec.ExternalTrafficPolicy != api.ServiceExternalTrafficPolicyLocal {
   670  		return false
   671  	}
   672  	return true
   673  }