github.com/cilium/cilium@v1.16.2/pkg/endpoint/api.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  // This file contains functions related to conversion of information about
     5  // an Endpoint to its corresponding Cilium API representation.
     6  
     7  package endpoint
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"net/netip"
    13  	"sort"
    14  	"strconv"
    15  
    16  	"github.com/sirupsen/logrus"
    17  
    18  	"github.com/cilium/cilium/api/v1/models"
    19  	"github.com/cilium/cilium/pkg/endpoint/regeneration"
    20  	"github.com/cilium/cilium/pkg/identity/cache"
    21  	identitymodel "github.com/cilium/cilium/pkg/identity/model"
    22  	"github.com/cilium/cilium/pkg/labels"
    23  	"github.com/cilium/cilium/pkg/labels/model"
    24  	"github.com/cilium/cilium/pkg/labelsfilter"
    25  	"github.com/cilium/cilium/pkg/mac"
    26  	"github.com/cilium/cilium/pkg/option"
    27  	"github.com/cilium/cilium/pkg/policy"
    28  	"github.com/cilium/cilium/pkg/types"
    29  	"github.com/cilium/cilium/pkg/u8proto"
    30  )
    31  
    32  // GetLabelsModel returns the labels of the endpoint in their representation
    33  // for the Cilium API. Returns an error if the Endpoint is being deleted.
    34  func (e *Endpoint) GetLabelsModel() (*models.LabelConfiguration, error) {
    35  	if err := e.rlockAlive(); err != nil {
    36  		return nil, err
    37  	}
    38  	spec := &models.LabelConfigurationSpec{
    39  		User: e.OpLabels.Custom.GetModel(),
    40  	}
    41  
    42  	cfg := models.LabelConfiguration{
    43  		Spec: spec,
    44  		Status: &models.LabelConfigurationStatus{
    45  			Realized:         spec,
    46  			SecurityRelevant: e.OpLabels.OrchestrationIdentity.GetModel(),
    47  			Derived:          e.OpLabels.OrchestrationInfo.GetModel(),
    48  			Disabled:         e.OpLabels.Disabled.GetModel(),
    49  		},
    50  	}
    51  	e.runlock()
    52  	return &cfg, nil
    53  }
    54  
    55  func parsePrefixOrAddr(ip string) (netip.Addr, error) {
    56  	prefix, err := netip.ParsePrefix(ip)
    57  	if err != nil {
    58  		return netip.ParseAddr(ip)
    59  	}
    60  	return prefix.Addr(), nil
    61  }
    62  
    63  // NewEndpointFromChangeModel creates a new endpoint from a request
    64  func NewEndpointFromChangeModel(ctx context.Context, owner regeneration.Owner, policyGetter policyRepoGetter, namedPortsGetter namedPortsGetter, proxy EndpointProxy, allocator cache.IdentityAllocator, base *models.EndpointChangeRequest) (*Endpoint, error) {
    65  	if base == nil {
    66  		return nil, nil
    67  	}
    68  
    69  	ep := createEndpoint(owner, policyGetter, namedPortsGetter, proxy, allocator, uint16(base.ID), base.InterfaceName)
    70  	ep.ifIndex = int(base.InterfaceIndex)
    71  	ep.containerIfName = base.ContainerInterfaceName
    72  	if base.ContainerName != "" {
    73  		ep.containerName.Store(&base.ContainerName)
    74  	}
    75  	if base.ContainerID != "" {
    76  		ep.containerID.Store(&base.ContainerID)
    77  	}
    78  	ep.dockerNetworkID = base.DockerNetworkID
    79  	ep.dockerEndpointID = base.DockerEndpointID
    80  	ep.K8sPodName = base.K8sPodName
    81  	ep.K8sNamespace = base.K8sNamespace
    82  	ep.K8sUID = base.K8sUID
    83  	ep.disableLegacyIdentifiers = base.DisableLegacyIdentifiers
    84  
    85  	if base.Mac != "" {
    86  		m, err := mac.ParseMAC(base.Mac)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		ep.mac = m
    91  	}
    92  
    93  	if base.HostMac != "" {
    94  		m, err := mac.ParseMAC(base.HostMac)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		ep.nodeMAC = m
    99  	}
   100  
   101  	if base.NetnsCookie != "" {
   102  		cookie64, err := strconv.ParseInt(base.NetnsCookie, 10, 64)
   103  		if err != nil {
   104  			// Don't return on error (and block the endpoint creation) as this
   105  			// is an unusual case where data could have been malformed. Defer error
   106  			// logging to individual features depending on the metadata.
   107  			log.WithError(err).WithFields(logrus.Fields{
   108  				"netns_cookie": base.NetnsCookie,
   109  				"ep_id":        base.ID,
   110  			}).Error("unable to parse netns cookie for ep")
   111  		} else {
   112  			ep.NetNsCookie = uint64(cookie64)
   113  		}
   114  	}
   115  
   116  	if base.Addressing != nil {
   117  		if ip := base.Addressing.IPV6; ip != "" {
   118  			ip6, err := parsePrefixOrAddr(ip)
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  			if !ip6.Is6() {
   123  				return nil, fmt.Errorf("invalid IPv6 address %q", ip)
   124  			}
   125  			ep.IPv6 = ip6
   126  			ep.IPv6IPAMPool = base.Addressing.IPV6PoolName
   127  		}
   128  
   129  		if ip := base.Addressing.IPV4; ip != "" {
   130  			ip4, err := parsePrefixOrAddr(ip)
   131  			if err != nil {
   132  				return nil, err
   133  			}
   134  			if !ip4.Is4() {
   135  				return nil, fmt.Errorf("invalid IPv4 address %q", ip)
   136  			}
   137  			ep.IPv4 = ip4
   138  			ep.IPv4IPAMPool = base.Addressing.IPV4PoolName
   139  		}
   140  	}
   141  
   142  	if base.DatapathConfiguration != nil {
   143  		ep.DatapathConfiguration = *base.DatapathConfiguration
   144  		// We need to make sure DatapathConfiguration.DisableSipVerification value
   145  		// overrides the value of SourceIPVerification runtime option of the endpoint.
   146  		if ep.DatapathConfiguration.DisableSipVerification {
   147  			ep.updateAndOverrideEndpointOptions(option.OptionMap{option.SourceIPVerification: option.OptionDisabled})
   148  		}
   149  	}
   150  
   151  	if base.Labels != nil {
   152  		lbls := labels.NewLabelsFromModel(base.Labels)
   153  		identityLabels, infoLabels := labelsfilter.Filter(lbls)
   154  		ep.OpLabels.OrchestrationIdentity = identityLabels
   155  		ep.OpLabels.OrchestrationInfo = infoLabels
   156  	}
   157  
   158  	if base.State != nil {
   159  		ep.setState(State(*base.State), "Endpoint creation")
   160  	}
   161  
   162  	if base.Properties != nil {
   163  		ep.properties = base.Properties
   164  	}
   165  
   166  	return ep, nil
   167  }
   168  
   169  func (e *Endpoint) getModelEndpointIdentitiersRLocked() *models.EndpointIdentifiers {
   170  	identifiers := &models.EndpointIdentifiers{
   171  		CniAttachmentID:  e.GetCNIAttachmentID(),
   172  		DockerEndpointID: e.dockerEndpointID,
   173  		DockerNetworkID:  e.dockerNetworkID,
   174  	}
   175  
   176  	// Use legacy endpoint identifiers only if the endpoint has not opted out
   177  	if !e.disableLegacyIdentifiers {
   178  		identifiers.ContainerID = e.GetContainerID()
   179  		identifiers.ContainerName = e.GetContainerName()
   180  		identifiers.PodName = e.GetK8sNamespaceAndPodName()
   181  		identifiers.K8sPodName = e.K8sPodName
   182  		identifiers.K8sNamespace = e.K8sNamespace
   183  	}
   184  
   185  	return identifiers
   186  }
   187  
   188  func (e *Endpoint) getModelNetworkingRLocked() *models.EndpointNetworking {
   189  	return &models.EndpointNetworking{
   190  		Addressing: []*models.AddressPair{{
   191  			IPV4:         e.GetIPv4Address(),
   192  			IPV4PoolName: e.IPv4IPAMPool,
   193  			IPV6:         e.GetIPv6Address(),
   194  			IPV6PoolName: e.IPv6IPAMPool,
   195  		}},
   196  		InterfaceIndex:         int64(e.ifIndex),
   197  		InterfaceName:          e.ifName,
   198  		ContainerInterfaceName: e.containerIfName,
   199  		Mac:                    e.mac.String(),
   200  		HostMac:                e.nodeMAC.String(),
   201  	}
   202  }
   203  
   204  func (e *Endpoint) getModelCurrentStateRLocked() models.EndpointState {
   205  	currentState := models.EndpointState(e.state)
   206  	if currentState == models.EndpointStateReady && e.status.CurrentStatus() != OK {
   207  		return models.EndpointStateNotDashReady
   208  	}
   209  	return currentState
   210  }
   211  
   212  // GetModelRLocked returns the API model of endpoint e.
   213  // e.mutex must be RLocked.
   214  func (e *Endpoint) GetModelRLocked() *models.Endpoint {
   215  	if e == nil {
   216  		return nil
   217  	}
   218  
   219  	// This returns the most recent log entry for this endpoint. It is backwards
   220  	// compatible with the json from before we added `cilium endpoint log` but it
   221  	// only returns 1 entry.
   222  	statusLog := e.status.GetModel()
   223  	if len(statusLog) > 0 {
   224  		statusLog = statusLog[:1]
   225  	}
   226  
   227  	lblMdl := model.NewModel(&e.OpLabels)
   228  
   229  	// Sort these slices since they come out in random orders. This allows
   230  	// reflect.DeepEqual to succeed.
   231  	sort.StringSlice(lblMdl.Realized.User).Sort()
   232  	sort.StringSlice(lblMdl.Disabled).Sort()
   233  	sort.StringSlice(lblMdl.SecurityRelevant).Sort()
   234  	sort.StringSlice(lblMdl.Derived).Sort()
   235  
   236  	controllerMdl := e.controllers.GetStatusModel()
   237  	sort.Slice(controllerMdl, func(i, j int) bool { return controllerMdl[i].Name < controllerMdl[j].Name })
   238  
   239  	spec := &models.EndpointConfigurationSpec{
   240  		LabelConfiguration: lblMdl.Realized,
   241  	}
   242  
   243  	if e.Options != nil {
   244  		spec.Options = *e.Options.GetMutableModel()
   245  	}
   246  
   247  	mdl := &models.Endpoint{
   248  		ID:   int64(e.ID),
   249  		Spec: spec,
   250  		Status: &models.EndpointStatus{
   251  			// FIXME GH-3280 When we begin implementing revision numbers this will
   252  			// diverge from models.Endpoint.Spec to reflect the in-datapath config
   253  			Realized:            spec,
   254  			Identity:            identitymodel.CreateModel(e.SecurityIdentity),
   255  			Labels:              lblMdl,
   256  			Networking:          e.getModelNetworkingRLocked(),
   257  			ExternalIdentifiers: e.getModelEndpointIdentitiersRLocked(),
   258  			// FIXME GH-3280 When we begin returning endpoint revisions this should
   259  			// change to return the configured and in-datapath policies.
   260  			Policy:      e.GetPolicyModel(),
   261  			Log:         statusLog,
   262  			Controllers: controllerMdl,
   263  			State:       e.getModelCurrentStateRLocked().Pointer(), // TODO: Validate
   264  			Health:      e.getHealthModel(),
   265  			NamedPorts:  e.getNamedPortsModel(),
   266  		},
   267  	}
   268  
   269  	return mdl
   270  }
   271  
   272  // GetHealthModel returns the endpoint's health object.
   273  //
   274  // Must be called with e.mutex RLock()ed.
   275  func (e *Endpoint) getHealthModel() *models.EndpointHealth {
   276  	// Duplicated from GetModelRLocked.
   277  	currentState := models.EndpointState(e.state)
   278  	if currentState == models.EndpointStateReady && e.status.CurrentStatus() != OK {
   279  		currentState = models.EndpointStateNotDashReady
   280  	}
   281  
   282  	h := models.EndpointHealth{
   283  		Bpf:           models.EndpointHealthStatusDisabled,
   284  		Policy:        models.EndpointHealthStatusDisabled,
   285  		Connected:     false,
   286  		OverallHealth: models.EndpointHealthStatusDisabled,
   287  	}
   288  	switch currentState {
   289  	case models.EndpointStateRegenerating, models.EndpointStateWaitingDashToDashRegenerate, models.EndpointStateDisconnecting:
   290  		h = models.EndpointHealth{
   291  			Bpf:           models.EndpointHealthStatusPending,
   292  			Policy:        models.EndpointHealthStatusPending,
   293  			Connected:     true,
   294  			OverallHealth: models.EndpointHealthStatusPending,
   295  		}
   296  	case models.EndpointStateWaitingDashForDashIdentity:
   297  		h = models.EndpointHealth{
   298  			Bpf:           models.EndpointHealthStatusDisabled,
   299  			Policy:        models.EndpointHealthStatusBootstrap,
   300  			Connected:     true,
   301  			OverallHealth: models.EndpointHealthStatusDisabled,
   302  		}
   303  	case models.EndpointStateNotDashReady:
   304  		h = models.EndpointHealth{
   305  			Bpf:           models.EndpointHealthStatusWarning,
   306  			Policy:        models.EndpointHealthStatusWarning,
   307  			Connected:     true,
   308  			OverallHealth: models.EndpointHealthStatusWarning,
   309  		}
   310  	case models.EndpointStateDisconnected:
   311  		h = models.EndpointHealth{
   312  			Bpf:           models.EndpointHealthStatusDisabled,
   313  			Policy:        models.EndpointHealthStatusDisabled,
   314  			Connected:     false,
   315  			OverallHealth: models.EndpointHealthStatusDisabled,
   316  		}
   317  	case models.EndpointStateReady:
   318  		h = models.EndpointHealth{
   319  			Bpf:           models.EndpointHealthStatusOK,
   320  			Policy:        models.EndpointHealthStatusOK,
   321  			Connected:     true,
   322  			OverallHealth: models.EndpointHealthStatusOK,
   323  		}
   324  	}
   325  
   326  	return &h
   327  }
   328  
   329  // GetHealthModel returns the endpoint's health object.
   330  func (e *Endpoint) GetHealthModel() *models.EndpointHealth {
   331  	// NOTE: Using rlock on mutex directly because getHealthModel handles removed endpoint properly
   332  	e.mutex.RLock()
   333  	defer e.mutex.RUnlock()
   334  	return e.getHealthModel()
   335  }
   336  
   337  // getNamedPortsModel returns the endpoint's NamedPorts object.
   338  func (e *Endpoint) getNamedPortsModel() (np models.NamedPorts) {
   339  	var k8sPorts types.NamedPortMap
   340  	if p := e.k8sPorts.Load(); p != nil {
   341  		k8sPorts = *p
   342  	}
   343  
   344  	// keep named ports ordered to avoid the unnecessary updates to
   345  	// kube-apiserver
   346  	names := make([]string, 0, len(k8sPorts))
   347  	for name := range k8sPorts {
   348  		names = append(names, name)
   349  	}
   350  	sort.Strings(names)
   351  
   352  	np = make(models.NamedPorts, 0, len(k8sPorts))
   353  	for _, name := range names {
   354  		value := k8sPorts[name]
   355  		np = append(np, &models.Port{
   356  			Name:     name,
   357  			Port:     value.Port,
   358  			Protocol: u8proto.U8proto(value.Proto).String(),
   359  		})
   360  	}
   361  	return np
   362  }
   363  
   364  // GetNamedPortsModel returns the endpoint's NamedPorts object.
   365  func (e *Endpoint) GetNamedPortsModel() models.NamedPorts {
   366  	if err := e.rlockAlive(); err != nil {
   367  		return nil
   368  	}
   369  	defer e.runlock()
   370  	return e.getNamedPortsModel()
   371  }
   372  
   373  // GetModel returns the API model of endpoint e.
   374  func (e *Endpoint) GetModel() *models.Endpoint {
   375  	if e == nil {
   376  		return nil
   377  	}
   378  	// NOTE: Using rlock on mutex directly because GetModelRLocked handles removed endpoint properly
   379  	e.mutex.RLock()
   380  	defer e.mutex.RUnlock()
   381  
   382  	return e.GetModelRLocked()
   383  }
   384  
   385  // GetPolicyModel returns the endpoint's policy as an API model.
   386  //
   387  // Must be called with e.mutex RLock()ed.
   388  func (e *Endpoint) GetPolicyModel() *models.EndpointPolicyStatus {
   389  	if e == nil {
   390  		return nil
   391  	}
   392  
   393  	if e.SecurityIdentity == nil {
   394  		return nil
   395  	}
   396  
   397  	realizedLog := log.WithField("map-name", "realized").Logger
   398  	realizedIngressIdentities, realizedEgressIdentities :=
   399  		e.realizedPolicy.GetPolicyMap().GetIdentities(realizedLog)
   400  
   401  	realizedDenyIngressIdentities, realizedDenyEgressIdentities :=
   402  		e.realizedPolicy.GetPolicyMap().GetDenyIdentities(realizedLog)
   403  
   404  	desiredLog := log.WithField("map-name", "desired").Logger
   405  	desiredIngressIdentities, desiredEgressIdentities :=
   406  		e.desiredPolicy.GetPolicyMap().GetIdentities(desiredLog)
   407  
   408  	desiredDenyIngressIdentities, desiredDenyEgressIdentities :=
   409  		e.desiredPolicy.GetPolicyMap().GetDenyIdentities(desiredLog)
   410  
   411  	policyEnabled := e.policyStatus()
   412  
   413  	e.proxyStatisticsMutex.RLock()
   414  	proxyStats := make([]*models.ProxyStatistics, 0, len(e.proxyStatistics))
   415  	for _, stats := range e.proxyStatistics {
   416  		proxyStats = append(proxyStats, stats.DeepCopy())
   417  	}
   418  	e.proxyStatisticsMutex.RUnlock()
   419  	sortProxyStats(proxyStats)
   420  
   421  	var (
   422  		realizedL4Policy *policy.L4Policy
   423  	)
   424  	if e.realizedPolicy != nil {
   425  		realizedL4Policy = &e.realizedPolicy.L4Policy
   426  	}
   427  
   428  	mdl := &models.EndpointPolicy{
   429  		ID: int64(e.SecurityIdentity.ID),
   430  		// This field should be removed.
   431  		Build:                    int64(e.policyRevision),
   432  		PolicyRevision:           int64(e.policyRevision),
   433  		AllowedIngressIdentities: realizedIngressIdentities,
   434  		AllowedEgressIdentities:  realizedEgressIdentities,
   435  		DeniedIngressIdentities:  realizedDenyIngressIdentities,
   436  		DeniedEgressIdentities:   realizedDenyEgressIdentities,
   437  		L4:                       realizedL4Policy.GetModel(),
   438  		PolicyEnabled:            policyEnabled,
   439  	}
   440  
   441  	var (
   442  		desiredL4Policy *policy.L4Policy
   443  	)
   444  	if e.desiredPolicy != nil {
   445  		desiredL4Policy = &e.desiredPolicy.L4Policy
   446  	}
   447  
   448  	desiredMdl := &models.EndpointPolicy{
   449  		ID: int64(e.SecurityIdentity.ID),
   450  		// This field should be removed.
   451  		Build:                    int64(e.nextPolicyRevision),
   452  		PolicyRevision:           int64(e.nextPolicyRevision),
   453  		AllowedIngressIdentities: desiredIngressIdentities,
   454  		AllowedEgressIdentities:  desiredEgressIdentities,
   455  		DeniedIngressIdentities:  desiredDenyIngressIdentities,
   456  		DeniedEgressIdentities:   desiredDenyEgressIdentities,
   457  		L4:                       desiredL4Policy.GetModel(),
   458  		PolicyEnabled:            policyEnabled,
   459  	}
   460  	// FIXME GH-3280 Once we start returning revisions Realized should be the
   461  	// policy implemented in the data path
   462  	return &models.EndpointPolicyStatus{
   463  		Spec:                desiredMdl,
   464  		Realized:            mdl,
   465  		ProxyPolicyRevision: int64(e.proxyPolicyRevision),
   466  		ProxyStatistics:     proxyStats,
   467  	}
   468  }
   469  
   470  // policyStatus returns the endpoint's policy status
   471  //
   472  // Must be called with e.mutex RLock()ed.
   473  func (e *Endpoint) policyStatus() models.EndpointPolicyEnabled {
   474  	policyEnabled := models.EndpointPolicyEnabledNone
   475  	switch {
   476  	case e.realizedPolicy.IngressPolicyEnabled && e.realizedPolicy.EgressPolicyEnabled:
   477  		policyEnabled = models.EndpointPolicyEnabledBoth
   478  	case e.realizedPolicy.IngressPolicyEnabled:
   479  		policyEnabled = models.EndpointPolicyEnabledIngress
   480  	case e.realizedPolicy.EgressPolicyEnabled:
   481  		policyEnabled = models.EndpointPolicyEnabledEgress
   482  	}
   483  
   484  	if e.Options.IsEnabled(option.PolicyAuditMode) {
   485  		switch policyEnabled {
   486  		case models.EndpointPolicyEnabledIngress:
   487  			return models.EndpointPolicyEnabledAuditDashIngress
   488  		case models.EndpointPolicyEnabledEgress:
   489  			return models.EndpointPolicyEnabledAuditDashEgress
   490  		case models.EndpointPolicyEnabledBoth:
   491  			return models.EndpointPolicyEnabledAuditDashBoth
   492  		}
   493  	}
   494  
   495  	return policyEnabled
   496  }
   497  
   498  // ProcessChangeRequest handles the update logic for performing a PATCH operation
   499  // on a given Endpoint. Returns the reason which will be used for informational
   500  // purposes should a caller choose to try to regenerate this endpoint, as well
   501  // as an error if the Endpoint is being deleted, since there is no point in
   502  // changing an Endpoint if it is going to be deleted.
   503  //
   504  // Before adding any new fields here, check to see if they are assumed to be mutable after
   505  // endpoint creation!
   506  func (e *Endpoint) ProcessChangeRequest(newEp *Endpoint, validPatchTransitionState bool) (string, error) {
   507  	var (
   508  		changed bool
   509  		reason  string
   510  	)
   511  
   512  	if err := e.lockAlive(); err != nil {
   513  		return "", err
   514  	}
   515  	defer e.unlock()
   516  
   517  	if newEp.ifIndex != 0 && e.ifIndex != newEp.ifIndex {
   518  		e.ifIndex = newEp.ifIndex
   519  		changed = true
   520  	}
   521  
   522  	if newEp.ifName != "" && e.ifName != newEp.ifName {
   523  		e.ifName = newEp.ifName
   524  		changed = true
   525  	}
   526  
   527  	// Only support transition to waiting-for-identity state, also
   528  	// if the request is for ready state, as we will check the
   529  	// existence of the security label below. Other transitions
   530  	// are always internally managed, but we do not error out for
   531  	// backwards compatibility.
   532  	if newEp.state != "" &&
   533  		validPatchTransitionState &&
   534  		e.getState() != StateWaitingForIdentity {
   535  		// Will not change state if the current state does not allow the transition.
   536  		if e.setState(StateWaitingForIdentity, "Update endpoint from API PATCH") {
   537  			changed = true
   538  		}
   539  	}
   540  
   541  	if newContainerName := newEp.containerName.Load(); newContainerName != nil && *newContainerName != "" {
   542  		e.containerName.Store(newContainerName)
   543  		// no need to set changed here
   544  	}
   545  
   546  	if newContainerID := newEp.containerID.Load(); newContainerID != nil && *newContainerID != "" {
   547  		e.containerID.Store(newContainerID)
   548  		// no need to set changed here
   549  	}
   550  
   551  	e.replaceInformationLabels(labels.LabelSourceAny, newEp.OpLabels.OrchestrationInfo)
   552  	rev := e.replaceIdentityLabels(labels.LabelSourceAny, newEp.OpLabels.IdentityLabels())
   553  	if rev != 0 {
   554  		// Run as a goroutine since the runIdentityResolver needs to get the lock
   555  		go e.runIdentityResolver(e.aliveCtx, false)
   556  	}
   557  
   558  	// If desired state is waiting-for-identity but identity is already
   559  	// known, bump it to ready state immediately to force re-generation
   560  	if newEp.state == StateWaitingForIdentity && e.SecurityIdentity != nil {
   561  		e.setState(StateReady, "Preparing to force endpoint regeneration because identity is known while handling API PATCH")
   562  		changed = true
   563  	}
   564  
   565  	if changed {
   566  		// Force policy regeneration as endpoint's configuration was changed.
   567  		// Other endpoints need not be regenerated as no labels were changed.
   568  		// Note that we still need to (eventually) regenerate the endpoint for
   569  		// the changes to take effect.
   570  		e.forcePolicyComputation()
   571  
   572  		// Transition to waiting-to-regenerate if ready.
   573  		if e.getState() == StateReady {
   574  			e.setState(StateWaitingToRegenerate, "Forcing endpoint regeneration because identity is known while handling API PATCH")
   575  		}
   576  
   577  		switch e.getState() {
   578  		case StateWaitingToRegenerate:
   579  			reason = "Waiting on endpoint regeneration because identity is known while handling API PATCH"
   580  		case StateWaitingForIdentity:
   581  			reason = "Waiting on endpoint initial program regeneration while handling API PATCH"
   582  		default:
   583  			// Caller skips regeneration if reason == "". Bump the skipped regeneration level so that next
   584  			// regeneration will realise endpoint changes.
   585  			if e.skippedRegenerationLevel < regeneration.RegenerateWithDatapath {
   586  				e.skippedRegenerationLevel = regeneration.RegenerateWithDatapath
   587  			}
   588  		}
   589  	}
   590  
   591  	e.UpdateLogger(nil)
   592  
   593  	return reason, nil
   594  }
   595  
   596  // GetConfigurationStatus returns the Cilium API representation of the
   597  // configuration of this endpoint.
   598  func (e *Endpoint) GetConfigurationStatus() *models.EndpointConfigurationStatus {
   599  	return &models.EndpointConfigurationStatus{
   600  		Realized: &models.EndpointConfigurationSpec{
   601  			LabelConfiguration: &models.LabelConfigurationSpec{
   602  				User: e.OpLabels.Custom.GetModel(),
   603  			},
   604  			Options: *e.Options.GetMutableModel(),
   605  		},
   606  		Immutable: *e.Options.GetImmutableModel(),
   607  	}
   608  }
   609  
   610  // ApplyUserLabelChanges changes the label configuration of the endpoint per the
   611  // provided labels. Returns labels that were added and deleted. Returns an
   612  // error if the endpoint is being deleted.
   613  func (e *Endpoint) ApplyUserLabelChanges(lbls labels.Labels) (add, del labels.Labels, err error) {
   614  	if err := e.rlockAlive(); err != nil {
   615  		return nil, nil, err
   616  	}
   617  	defer e.runlock()
   618  	add, del = e.OpLabels.SplitUserLabelChanges(lbls)
   619  	return
   620  }
   621  
   622  // GetStatusModel returns the model of the status of this endpoint.
   623  func (e *Endpoint) GetStatusModel() []*models.EndpointStatusChange {
   624  	return e.status.GetModel()
   625  }