feat: shim in metric for ipconfigstatus state transition durations (#1080)
Signed-off-by: Evan Baker <rbtr@users.noreply.github.com>
This commit is contained in:
Родитель
5a477a9f89
Коммит
9fff334a19
|
@ -149,6 +149,8 @@ type PodInfo interface {
|
|||
Namespace() string
|
||||
// OrchestratorContext is a JSON KubernetesPodInfo
|
||||
OrchestratorContext() (json.RawMessage, error)
|
||||
// Equals implements a functional equals for PodInfos
|
||||
Equals(PodInfo) bool
|
||||
}
|
||||
|
||||
type KubernetesPodInfo struct {
|
||||
|
@ -166,6 +168,16 @@ type podInfo struct {
|
|||
Version podInfoScheme
|
||||
}
|
||||
|
||||
func (p *podInfo) Equals(o PodInfo) bool {
|
||||
if (p == nil) != (o == nil) {
|
||||
return false
|
||||
}
|
||||
if p == nil {
|
||||
return true
|
||||
}
|
||||
return p.Key() == o.Key()
|
||||
}
|
||||
|
||||
func (p *podInfo) InfraContainerID() string {
|
||||
return p.PodInfraContainerID
|
||||
}
|
||||
|
|
86
cns/api.go
86
cns/api.go
|
@ -7,10 +7,12 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-container-networking/cns/common"
|
||||
"github.com/Azure/azure-container-networking/cns/types"
|
||||
"github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Container Network Service remote API Contract
|
||||
|
@ -51,16 +53,70 @@ type HTTPService interface {
|
|||
// This is used for KubernetesCRD orchestrator Type where NC has multiple ips.
|
||||
// This struct captures the state for SecondaryIPs associated to a given NC
|
||||
type IPConfigurationStatus struct {
|
||||
NCID string
|
||||
ID string // uuid
|
||||
IPAddress string
|
||||
State types.IPState
|
||||
PodInfo PodInfo
|
||||
ID string // uuid
|
||||
IPAddress string
|
||||
LastStateTransition time.Time
|
||||
NCID string
|
||||
PodInfo PodInfo
|
||||
state types.IPState
|
||||
stateMiddlewareFuncs []stateMiddlewareFunc
|
||||
}
|
||||
|
||||
func (i IPConfigurationStatus) String() string {
|
||||
// Equals compares a subset of the IPConfigurationStatus fields since a direct
|
||||
// DeepEquals or otherwise complete comparison of two IPConfigurationStatus objects
|
||||
// compares internal state details that don't impact their functional equality.
|
||||
//nolint:gocritic // it's safer to pass this by value
|
||||
func (i *IPConfigurationStatus) Equals(o IPConfigurationStatus) bool {
|
||||
if i.PodInfo != nil && o.PodInfo != nil {
|
||||
if !i.PodInfo.Equals(o.PodInfo) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return i.ID == o.ID &&
|
||||
i.IPAddress == o.IPAddress &&
|
||||
i.NCID == o.NCID &&
|
||||
i.state == o.state
|
||||
}
|
||||
|
||||
func (i *IPConfigurationStatus) GetState() types.IPState {
|
||||
return i.state
|
||||
}
|
||||
|
||||
type stateMiddlewareFunc func(*IPConfigurationStatus, types.IPState)
|
||||
|
||||
func (i *IPConfigurationStatus) SetState(s types.IPState) {
|
||||
for _, f := range i.stateMiddlewareFuncs {
|
||||
f(i, s)
|
||||
}
|
||||
i.LastStateTransition = time.Now()
|
||||
i.state = s
|
||||
}
|
||||
|
||||
func (i *IPConfigurationStatus) WithStateMiddleware(fs ...stateMiddlewareFunc) {
|
||||
i.stateMiddlewareFuncs = append(i.stateMiddlewareFuncs, fs...)
|
||||
}
|
||||
|
||||
func (i *IPConfigurationStatus) String() string {
|
||||
return fmt.Sprintf("IPConfigurationStatus: Id: [%s], NcId: [%s], IpAddress: [%s], State: [%s], PodInfo: [%v]",
|
||||
i.ID, i.NCID, i.IPAddress, i.State, i.PodInfo)
|
||||
i.ID, i.NCID, i.IPAddress, i.state, i.PodInfo)
|
||||
}
|
||||
|
||||
// MarshalJSON is a custom marshaller for IPConfigurationStatus that
|
||||
// is capable of marshalling the private fields in the struct. The default
|
||||
// marshaller can't see private fields by default, so we alias the type through
|
||||
// a struct that has public fields for the original struct's private fields,
|
||||
// embed the original struct in an anonymous struct as the alias type, and then
|
||||
// let the default marshaller do its magic.
|
||||
//nolint:gocritic // ignore hugeParam it's a value receiver on purpose
|
||||
func (i IPConfigurationStatus) MarshalJSON() ([]byte, error) {
|
||||
type alias IPConfigurationStatus
|
||||
return json.Marshal(&struct { //nolint:wrapcheck // MarshalJSON is not called by us
|
||||
State types.IPState `json:"state"`
|
||||
*alias
|
||||
}{
|
||||
State: i.state,
|
||||
alias: (*alias)(&i),
|
||||
})
|
||||
}
|
||||
|
||||
// UnmarshalJSON is a custom unmarshaller for IPConfigurationStatus that
|
||||
|
@ -70,32 +126,32 @@ func (i IPConfigurationStatus) String() string {
|
|||
func (i *IPConfigurationStatus) UnmarshalJSON(b []byte) error {
|
||||
m := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to unmarshal to RawMessage")
|
||||
}
|
||||
if s, ok := m["NCID"]; ok {
|
||||
if err := json.Unmarshal(s, &(i.NCID)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to unmarshal key NCID to string")
|
||||
}
|
||||
}
|
||||
if s, ok := m["ID"]; ok {
|
||||
if err := json.Unmarshal(s, &(i.ID)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to unmarshal key ID to string")
|
||||
}
|
||||
}
|
||||
if s, ok := m["IPAddress"]; ok {
|
||||
if err := json.Unmarshal(s, &(i.IPAddress)); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to unmarshal key IPAddress to string")
|
||||
}
|
||||
}
|
||||
if s, ok := m["State"]; ok {
|
||||
if err := json.Unmarshal(s, &(i.State)); err != nil {
|
||||
return err
|
||||
if s, ok := m["state"]; ok {
|
||||
if err := json.Unmarshal(s, &(i.state)); err != nil {
|
||||
return errors.Wrap(err, "failed to unmarshal key state to IPConfigState")
|
||||
}
|
||||
}
|
||||
if s, ok := m["PodInfo"]; ok {
|
||||
pi, err := UnmarshalPodInfo(s)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to unmarshal key PodInfo to PodInfo")
|
||||
}
|
||||
i.PodInfo = pi
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
@ -388,12 +390,17 @@ func (c *Client) GetHTTPServiceData(ctx context.Context) (*restserver.GetHTTPSer
|
|||
return nil, errors.Wrap(err, "http request failed")
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := io.ReadAll(res.Body)
|
||||
s := string(b)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("failed to read body %s", s))
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return nil, errors.Errorf("http response %d", res.StatusCode)
|
||||
}
|
||||
var resp restserver.GetHTTPServiceDataResponse
|
||||
err = json.NewDecoder(res.Body).Decode(&resp)
|
||||
err = json.NewDecoder(bytes.NewReader(b)).Decode(&resp)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode GetHTTPServiceDataResponse")
|
||||
}
|
||||
|
|
|
@ -255,7 +255,7 @@ func TestCNSClientRequestAndRelease(t *testing.T) {
|
|||
|
||||
secondaryIps := make([]string, 0)
|
||||
secondaryIps = append(secondaryIps, desiredIpAddress)
|
||||
cnsClient, _ := New("", 2*time.Second)
|
||||
cnsClient, _ := New("", 2*time.Hour)
|
||||
|
||||
addTestStateToRestServer(t, secondaryIps)
|
||||
|
||||
|
@ -289,7 +289,7 @@ func TestCNSClientRequestAndRelease(t *testing.T) {
|
|||
|
||||
assert.Len(t, ipaddresses, 1, "Number of available IP addresses expected to be 1")
|
||||
assert.Equal(t, desiredIpAddress, ipaddresses[0].IPAddress, "Available IP address does not match expected, address state")
|
||||
assert.Equal(t, types.Assigned, ipaddresses[0].State, "Available IP address does not match expected, address state")
|
||||
assert.Equal(t, types.Assigned, ipaddresses[0].GetState(), "Available IP address does not match expected, address state")
|
||||
|
||||
t.Log(ipaddresses)
|
||||
|
||||
|
@ -334,7 +334,7 @@ func TestCNSClientDebugAPI(t *testing.T) {
|
|||
desiredIpAddress := "10.0.0.5"
|
||||
|
||||
secondaryIps := []string{desiredIpAddress}
|
||||
cnsClient, _ := New("", 2*time.Second)
|
||||
cnsClient, _ := New("", 2*time.Hour)
|
||||
|
||||
addTestStateToRestServer(t, secondaryIps)
|
||||
|
||||
|
@ -356,7 +356,7 @@ func TestCNSClientDebugAPI(t *testing.T) {
|
|||
podConfig := inmemory.HTTPRestServiceData.PodIPConfigState
|
||||
for _, v := range podConfig {
|
||||
assert.Equal(t, "10.0.0.5", v.IPAddress, "Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
|
||||
assert.Equal(t, types.Assigned, v.State, "Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
|
||||
assert.Equal(t, types.Assigned, v.GetState(), "Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
|
||||
assert.Equal(t, "testNcId1", v.NCID, "Not the expected set values for testing IPConfigurationStatus, %+v", podConfig)
|
||||
}
|
||||
assert.GreaterOrEqual(t, len(inmemory.HTTPRestServiceData.PodIPConfigState), 1, "PodIpConfigState with at least 1 entry expected")
|
||||
|
|
|
@ -68,7 +68,7 @@ func (ipm *IPStateManager) AddIPConfigs(ipconfigs []cns.IPConfigurationStatus) {
|
|||
ipm.Lock()
|
||||
defer ipm.Unlock()
|
||||
for _, ipconfig := range ipconfigs {
|
||||
switch ipconfig.State {
|
||||
switch ipconfig.GetState() {
|
||||
case types.PendingProgramming:
|
||||
ipm.PendingProgramIPConfigState[ipconfig.ID] = ipconfig
|
||||
case types.Available:
|
||||
|
@ -98,7 +98,7 @@ func (ipm *IPStateManager) ReserveIPConfig() (cns.IPConfigurationStatus, error)
|
|||
return cns.IPConfigurationStatus{}, err
|
||||
}
|
||||
ipc := ipm.AvailableIPConfigState[id]
|
||||
ipc.State = types.Assigned
|
||||
ipc.SetState(types.Assigned)
|
||||
ipm.AssignedIPConfigState[id] = ipc
|
||||
delete(ipm.AvailableIPConfigState, id)
|
||||
return ipm.AssignedIPConfigState[id], nil
|
||||
|
@ -108,7 +108,7 @@ func (ipm *IPStateManager) ReleaseIPConfig(ipconfigID string) (cns.IPConfigurati
|
|||
ipm.Lock()
|
||||
defer ipm.Unlock()
|
||||
ipc := ipm.AssignedIPConfigState[ipconfigID]
|
||||
ipc.State = types.Available
|
||||
ipc.SetState(types.Available)
|
||||
ipm.AvailableIPConfigState[ipconfigID] = ipc
|
||||
ipm.AvailableIPIDStack.Push(ipconfigID)
|
||||
delete(ipm.AssignedIPConfigState, ipconfigID)
|
||||
|
@ -127,7 +127,7 @@ func (ipm *IPStateManager) MarkIPAsPendingRelease(numberOfIPsToMark int) (map[st
|
|||
// if there was an error, and not all ip's have been freed, restore state
|
||||
if err != nil && len(pendingReleaseIPs) != numberOfIPsToMark {
|
||||
for uuid, ipState := range pendingReleaseIPs {
|
||||
ipState.State = types.Available
|
||||
ipState.SetState(types.Available)
|
||||
ipm.AvailableIPIDStack.Push(pendingReleaseIPs[uuid].ID)
|
||||
ipm.AvailableIPConfigState[pendingReleaseIPs[uuid].ID] = ipState
|
||||
delete(ipm.PendingReleaseIPConfigState, pendingReleaseIPs[uuid].ID)
|
||||
|
@ -143,7 +143,7 @@ func (ipm *IPStateManager) MarkIPAsPendingRelease(numberOfIPsToMark int) (map[st
|
|||
|
||||
// add all pending release to a slice
|
||||
ipConfig := ipm.AvailableIPConfigState[id]
|
||||
ipConfig.State = types.PendingRelease
|
||||
ipConfig.SetState(types.PendingRelease)
|
||||
pendingReleaseIPs[id] = ipConfig
|
||||
|
||||
delete(ipm.AvailableIPConfigState, id)
|
||||
|
|
|
@ -56,8 +56,8 @@ func (rc *RequestControllerFake) CarveIPConfigsAndAddToStatusAndCNS(numberOfIPCo
|
|||
ipconfigCNS := cns.IPConfigurationStatus{
|
||||
ID: ipconfigCRD.Name,
|
||||
IPAddress: ipconfigCRD.IP,
|
||||
State: types.Available,
|
||||
}
|
||||
ipconfigCNS.SetState(types.Available)
|
||||
cnsIPConfigs = append(cnsIPConfigs, ipconfigCNS)
|
||||
|
||||
incrementIP(rc.ip)
|
||||
|
|
|
@ -29,7 +29,7 @@ var filters = map[types.IPState]IPConfigStatePredicate{
|
|||
// the passed State string and returns true when equal.
|
||||
func ipConfigStatePredicate(test types.IPState) IPConfigStatePredicate {
|
||||
return func(ipconfig cns.IPConfigurationStatus) bool {
|
||||
return ipconfig.State == test
|
||||
return ipconfig.GetState() == test
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,34 +16,33 @@ var testStatuses = []struct {
|
|||
{
|
||||
State: types.Assigned,
|
||||
Status: cns.IPConfigurationStatus{
|
||||
ID: "assigned",
|
||||
State: types.Assigned,
|
||||
ID: "assigned",
|
||||
},
|
||||
},
|
||||
{
|
||||
State: types.Available,
|
||||
Status: cns.IPConfigurationStatus{
|
||||
ID: "available",
|
||||
State: types.Available,
|
||||
ID: "available",
|
||||
},
|
||||
},
|
||||
{
|
||||
State: types.PendingProgramming,
|
||||
Status: cns.IPConfigurationStatus{
|
||||
ID: "pending-programming",
|
||||
State: types.PendingProgramming,
|
||||
ID: "pending-programming",
|
||||
},
|
||||
},
|
||||
{
|
||||
State: types.PendingRelease,
|
||||
Status: cns.IPConfigurationStatus{
|
||||
ID: "pending-release",
|
||||
State: types.PendingRelease,
|
||||
ID: "pending-release",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMatchesAnyIPConfigState(t *testing.T) {
|
||||
for i := range testStatuses {
|
||||
testStatuses[i].Status.SetState(testStatuses[i].State)
|
||||
}
|
||||
for i := range testStatuses {
|
||||
status := testStatuses[i].Status
|
||||
failStatus := testStatuses[(i+1)%len(testStatuses)].Status
|
||||
|
@ -54,6 +53,9 @@ func TestMatchesAnyIPConfigState(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMatchAnyIPConfigState(t *testing.T) {
|
||||
for i := range testStatuses {
|
||||
testStatuses[i].Status.SetState(testStatuses[i].State)
|
||||
}
|
||||
m := map[string]cns.IPConfigurationStatus{}
|
||||
for i := range testStatuses {
|
||||
key := strconv.Itoa(i)
|
||||
|
|
|
@ -126,7 +126,7 @@ func buildIPPoolState(ips map[string]cns.IPConfigurationStatus, spec v1alpha.Nod
|
|||
requested: spec.RequestedIPCount,
|
||||
}
|
||||
for _, v := range ips {
|
||||
switch v.State {
|
||||
switch v.GetState() {
|
||||
case types.Assigned:
|
||||
state.assigned++
|
||||
case types.Available:
|
||||
|
|
|
@ -154,8 +154,8 @@ func TestPendingIPsGotUpdatedWhenSyncHostNCVersion(t *testing.T) {
|
|||
}
|
||||
for i := range receivedSecondaryIPConfigs {
|
||||
podIPConfigState := svc.PodIPConfigState[i]
|
||||
if podIPConfigState.State != types.PendingProgramming {
|
||||
t.Errorf("Unexpected State %s, expeted State is %s, received %s, IP address is %s", podIPConfigState.State, types.PendingProgramming, podIPConfigState.State, podIPConfigState.IPAddress)
|
||||
if podIPConfigState.GetState() != types.PendingProgramming {
|
||||
t.Errorf("Unexpected State %s, expected State is %s, IP address is %s", podIPConfigState.GetState(), types.PendingProgramming, podIPConfigState.IPAddress)
|
||||
}
|
||||
}
|
||||
svc.SyncHostNCVersion(context.Background(), cns.CRD)
|
||||
|
@ -167,8 +167,8 @@ func TestPendingIPsGotUpdatedWhenSyncHostNCVersion(t *testing.T) {
|
|||
}
|
||||
for i := range receivedSecondaryIPConfigs {
|
||||
podIPConfigState := svc.PodIPConfigState[i]
|
||||
if podIPConfigState.State != types.Available {
|
||||
t.Errorf("Unexpected State %s, expeted State is %s, received %s, IP address is %s", podIPConfigState.State, types.Available, podIPConfigState.State, podIPConfigState.IPAddress)
|
||||
if podIPConfigState.GetState() != types.Available {
|
||||
t.Errorf("Unexpected State %s, expeted State is %s, IP address is %s", podIPConfigState.GetState(), types.Available, podIPConfigState.IPAddress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -483,15 +483,15 @@ func validateNetworkRequest(t *testing.T, req cns.CreateNetworkContainerRequest)
|
|||
// Validate IP state
|
||||
if ipStatus.PodInfo != nil {
|
||||
if _, exists := svc.PodIPIDByPodInterfaceKey[ipStatus.PodInfo.Key()]; exists {
|
||||
if ipStatus.State != types.Assigned {
|
||||
if ipStatus.GetState() != types.Assigned {
|
||||
t.Fatalf("IPId: %s State is not Assigned, ipStatus: %+v", ipid, ipStatus)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Failed to find podContext for assigned ip: %+v, podinfo :%+v", ipStatus, ipStatus.PodInfo)
|
||||
}
|
||||
} else if ipStatus.State != expectedIPStatus {
|
||||
} else if ipStatus.GetState() != expectedIPStatus {
|
||||
// Todo: Validate for pendingRelease as well
|
||||
t.Fatalf("IPId: %s State is not as expected, ipStatus is : %+v, expected status is %+v", ipid, ipStatus.State, expectedIPStatus)
|
||||
t.Fatalf("IPId: %s State is not as expected, ipStatus is : %+v, expected status is %+v", ipid, ipStatus.GetState(), expectedIPStatus)
|
||||
}
|
||||
|
||||
alreadyValidated[ipid] = ipStatus.IPAddress
|
||||
|
@ -552,7 +552,7 @@ func validateNCStateAfterReconcile(t *testing.T, ncRequest *cns.CreateNetworkCon
|
|||
ipId := svc.PodIPIDByPodInterfaceKey[podInfo.Key()]
|
||||
ipConfigstate := svc.PodIPConfigState[ipId]
|
||||
|
||||
if ipConfigstate.State != types.Assigned {
|
||||
if ipConfigstate.GetState() != types.Assigned {
|
||||
t.Fatalf("IpAddress %s is not marked as assigned to Pod: %+v, ipState: %+v", ipaddress, podInfo, ipConfigstate)
|
||||
}
|
||||
|
||||
|
@ -582,7 +582,7 @@ func validateNCStateAfterReconcile(t *testing.T, ncRequest *cns.CreateNetworkCon
|
|||
|
||||
// Validate IP state
|
||||
if secIpConfigState, found := svc.PodIPConfigState[secIpId]; found {
|
||||
if secIpConfigState.State != types.Available {
|
||||
if secIpConfigState.GetState() != types.Available {
|
||||
t.Fatalf("IPId: %s State is not Available, ipStatus: %+v", secIpId, secIpConfigState)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -112,7 +112,7 @@ func (service *HTTPRestService) MarkIPAsPendingRelease(totalIpsToRelease int) (m
|
|||
defer service.Unlock()
|
||||
|
||||
for uuid, existingIpConfig := range service.PodIPConfigState {
|
||||
if existingIpConfig.State == types.PendingProgramming {
|
||||
if existingIpConfig.GetState() == types.PendingProgramming {
|
||||
updatedIPConfig, err := service.updateIPConfigState(uuid, types.PendingRelease, existingIpConfig.PodInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -127,7 +127,7 @@ func (service *HTTPRestService) MarkIPAsPendingRelease(totalIpsToRelease int) (m
|
|||
|
||||
// if not all expected IPs are set to PendingRelease, then check the Available IPs
|
||||
for uuid, existingIpConfig := range service.PodIPConfigState {
|
||||
if existingIpConfig.State == types.Available {
|
||||
if existingIpConfig.GetState() == types.Available {
|
||||
updatedIPConfig, err := service.updateIPConfigState(uuid, types.PendingRelease, existingIpConfig.PodInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -148,7 +148,7 @@ func (service *HTTPRestService) MarkIPAsPendingRelease(totalIpsToRelease int) (m
|
|||
func (service *HTTPRestService) updateIPConfigState(ipID string, updatedState types.IPState, podInfo cns.PodInfo) (cns.IPConfigurationStatus, error) {
|
||||
if ipConfig, found := service.PodIPConfigState[ipID]; found {
|
||||
logger.Printf("[updateIPConfigState] Changing IpId [%s] state to [%s], podInfo [%+v]. Current config [%+v]", ipID, updatedState, podInfo, ipConfig)
|
||||
ipConfig.State = updatedState
|
||||
ipConfig.SetState(updatedState)
|
||||
ipConfig.PodInfo = podInfo
|
||||
service.PodIPConfigState[ipID] = ipConfig
|
||||
return ipConfig, nil
|
||||
|
@ -175,7 +175,7 @@ func (service *HTTPRestService) MarkIpsAsAvailableUntransacted(ncID string, newH
|
|||
for uuid, secondaryIPConfigs := range ncInfo.CreateNetworkContainerRequest.SecondaryIPConfigs {
|
||||
if ipConfigStatus, exist := service.PodIPConfigState[uuid]; !exist {
|
||||
logger.Errorf("IP %s with uuid as %s exist in service state Secondary IP list but can't find in PodIPConfigState", ipConfigStatus.IPAddress, uuid)
|
||||
} else if ipConfigStatus.State == types.PendingProgramming && secondaryIPConfigs.NCVersion <= newHostNCVersion {
|
||||
} else if ipConfigStatus.GetState() == types.PendingProgramming && secondaryIPConfigs.NCVersion <= newHostNCVersion {
|
||||
_, err := service.updateIPConfigState(uuid, types.Available, nil)
|
||||
if err != nil {
|
||||
logger.Errorf("Error updating IPConfig [%+v] state to Available, err: %+v", ipConfigStatus, err)
|
||||
|
@ -338,12 +338,12 @@ func (service *HTTPRestService) MarkExistingIPsAsPending(pendingIPIDs []string)
|
|||
|
||||
for _, id := range pendingIPIDs {
|
||||
if ipconfig, exists := service.PodIPConfigState[id]; exists {
|
||||
if ipconfig.State == types.Assigned {
|
||||
if ipconfig.GetState() == types.Assigned {
|
||||
return errors.Errorf("Failed to mark IP [%v] as pending, currently assigned", id)
|
||||
}
|
||||
|
||||
logger.Printf("[MarkExistingIPsAsPending]: Marking IP [%+v] to PendingRelease", ipconfig)
|
||||
ipconfig.State = types.PendingRelease
|
||||
ipconfig.SetState(types.PendingRelease)
|
||||
service.PodIPConfigState[id] = ipconfig
|
||||
} else {
|
||||
logger.Errorf("Inconsistent state, ipconfig with ID [%v] marked as pending release, but does not exist in state", id)
|
||||
|
@ -382,7 +382,7 @@ func (service *HTTPRestService) AssignDesiredIPConfig(podInfo cns.PodInfo, desir
|
|||
|
||||
for _, ipConfig := range service.PodIPConfigState {
|
||||
if ipConfig.IPAddress == desiredIPAddress {
|
||||
switch ipConfig.State { //nolint:exhaustive // ignoring PendingRelease case intentionally
|
||||
switch ipConfig.GetState() { //nolint:exhaustive // ignoring PendingRelease case intentionally
|
||||
case types.Assigned:
|
||||
// This IP has already been assigned, if it is assigned to same pod, then return the same
|
||||
// IPconfiguration
|
||||
|
@ -412,7 +412,7 @@ func (service *HTTPRestService) AssignAnyAvailableIPConfig(podInfo cns.PodInfo)
|
|||
defer service.Unlock()
|
||||
|
||||
for _, ipState := range service.PodIPConfigState {
|
||||
if ipState.State == types.Available {
|
||||
if ipState.GetState() == types.Available {
|
||||
if err := service.assignIPConfig(ipState, podInfo); err != nil {
|
||||
return cns.PodIpInfo{}, err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
package restserver
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
|
@ -13,6 +12,8 @@ import (
|
|||
"github.com/Azure/azure-container-networking/cns/fakes"
|
||||
"github.com/Azure/azure-container-networking/cns/types"
|
||||
"github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -53,77 +54,49 @@ func newSecondaryIPConfig(ipAddress string, ncVersion int) cns.SecondaryIPConfig
|
|||
|
||||
func NewPodState(ipaddress string, prefixLength uint8, id, ncid string, state types.IPState, ncVersion int) cns.IPConfigurationStatus {
|
||||
ipconfig := newSecondaryIPConfig(ipaddress, ncVersion)
|
||||
|
||||
return cns.IPConfigurationStatus{
|
||||
status := &cns.IPConfigurationStatus{
|
||||
IPAddress: ipconfig.IPAddress,
|
||||
ID: id,
|
||||
NCID: ncid,
|
||||
State: state,
|
||||
}
|
||||
status.SetState(state)
|
||||
return *status
|
||||
}
|
||||
|
||||
func requestIpAddressAndGetState(t *testing.T, req cns.IPConfigRequest) (cns.IPConfigurationStatus, error) {
|
||||
var (
|
||||
ipState cns.IPConfigurationStatus
|
||||
PodIpInfo cns.PodIpInfo
|
||||
err error
|
||||
)
|
||||
|
||||
PodIpInfo, err = requestIPConfigHelper(svc, req)
|
||||
func requestIPAddressAndGetState(t *testing.T, req cns.IPConfigRequest) (cns.IPConfigurationStatus, error) {
|
||||
PodIPInfo, err := requestIPConfigHelper(svc, req)
|
||||
if err != nil {
|
||||
return ipState, err
|
||||
return cns.IPConfigurationStatus{}, err
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(PodIpInfo.NetworkContainerPrimaryIPConfig.IPSubnet.IPAddress, primaryIp) != true {
|
||||
t.Fatalf("PrimarIP is not added as expected ipConfig %+v, expected primaryIP: %+v", PodIpInfo.NetworkContainerPrimaryIPConfig, primaryIp)
|
||||
}
|
||||
|
||||
if PodIpInfo.NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength != subnetPrfixLength {
|
||||
t.Fatalf("Primary IP Prefix length is not added as expected ipConfig %+v, expected: %+v", PodIpInfo.NetworkContainerPrimaryIPConfig, subnetPrfixLength)
|
||||
}
|
||||
|
||||
// validate DnsServer and Gateway Ip as the same configured for Primary IP
|
||||
if reflect.DeepEqual(PodIpInfo.NetworkContainerPrimaryIPConfig.DNSServers, dnsservers) != true {
|
||||
t.Fatalf("DnsServer is not added as expected ipConfig %+v, expected dnsServers: %+v", PodIpInfo.NetworkContainerPrimaryIPConfig, dnsservers)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(PodIpInfo.NetworkContainerPrimaryIPConfig.GatewayIPAddress, gatewayIp) != true {
|
||||
t.Fatalf("Gateway is not added as expected ipConfig %+v, expected GatewayIp: %+v", PodIpInfo.NetworkContainerPrimaryIPConfig, gatewayIp)
|
||||
}
|
||||
|
||||
if PodIpInfo.PodIPConfig.PrefixLength != subnetPrfixLength {
|
||||
t.Fatalf("Pod IP Prefix length is not added as expected ipConfig %+v, expected: %+v", PodIpInfo.PodIPConfig, subnetPrfixLength)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(PodIpInfo.HostPrimaryIPInfo.PrimaryIP, fakes.HostPrimaryIP) != true {
|
||||
t.Fatalf("Host PrimaryIP is not added as expected ipConfig %+v, expected primaryIP: %+v", PodIpInfo.HostPrimaryIPInfo, fakes.HostPrimaryIP)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(PodIpInfo.HostPrimaryIPInfo.Subnet, fakes.HostSubnet) != true {
|
||||
t.Fatalf("Host Subnet is not added as expected ipConfig %+v, expected Host subnet: %+v", PodIpInfo.HostPrimaryIPInfo, fakes.HostSubnet)
|
||||
}
|
||||
assert.Equal(t, primaryIp, PodIPInfo.NetworkContainerPrimaryIPConfig.IPSubnet.IPAddress)
|
||||
assert.Equal(t, subnetPrfixLength, int(PodIPInfo.NetworkContainerPrimaryIPConfig.IPSubnet.PrefixLength))
|
||||
assert.Equal(t, dnsservers, PodIPInfo.NetworkContainerPrimaryIPConfig.DNSServers)
|
||||
assert.Equal(t, gatewayIp, PodIPInfo.NetworkContainerPrimaryIPConfig.GatewayIPAddress)
|
||||
assert.Equal(t, subnetPrfixLength, int(PodIPInfo.PodIPConfig.PrefixLength))
|
||||
assert.Equal(t, fakes.HostPrimaryIP, PodIPInfo.HostPrimaryIPInfo.PrimaryIP)
|
||||
assert.Equal(t, fakes.HostSubnet, PodIPInfo.HostPrimaryIPInfo.Subnet)
|
||||
|
||||
// retrieve podinfo from orchestrator context
|
||||
podInfo, err := cns.UnmarshalPodInfo(req.OrchestratorContext)
|
||||
if err != nil {
|
||||
return ipState, err
|
||||
return cns.IPConfigurationStatus{}, errors.Wrap(err, "failed to unmarshal pod info")
|
||||
}
|
||||
|
||||
ipId := svc.PodIPIDByPodInterfaceKey[podInfo.Key()]
|
||||
ipState = svc.PodIPConfigState[ipId]
|
||||
|
||||
return ipState, err
|
||||
ipID := svc.PodIPIDByPodInterfaceKey[podInfo.Key()]
|
||||
return svc.PodIPConfigState[ipID], nil
|
||||
}
|
||||
|
||||
func NewPodStateWithOrchestratorContext(ipaddress, id, ncid string, state types.IPState, prefixLength uint8, ncVersion int, podInfo cns.PodInfo) (cns.IPConfigurationStatus, error) {
|
||||
ipconfig := newSecondaryIPConfig(ipaddress, ncVersion)
|
||||
return cns.IPConfigurationStatus{
|
||||
status := &cns.IPConfigurationStatus{
|
||||
IPAddress: ipconfig.IPAddress,
|
||||
ID: id,
|
||||
NCID: ncid,
|
||||
State: state,
|
||||
PodInfo: podInfo,
|
||||
}, nil
|
||||
}
|
||||
status.SetState(state)
|
||||
return *status, nil
|
||||
}
|
||||
|
||||
// Test function to populate the IPConfigState
|
||||
|
@ -144,7 +117,7 @@ func UpdatePodIpConfigState(t *testing.T, svc *HTTPRestService, ipconfigs map[st
|
|||
|
||||
// update ipconfigs to expected state
|
||||
for ipId, ipconfig := range ipconfigs {
|
||||
if ipconfig.State == types.Assigned {
|
||||
if ipconfig.GetState() == types.Assigned {
|
||||
svc.PodIPIDByPodInterfaceKey[ipconfig.PodInfo.Key()] = ipId
|
||||
svc.PodIPConfigState[ipId] = ipconfig
|
||||
}
|
||||
|
@ -169,7 +142,7 @@ func TestIPAMGetAvailableIPConfig(t *testing.T) {
|
|||
b, _ := testPod1Info.OrchestratorContext()
|
||||
req.OrchestratorContext = b
|
||||
|
||||
actualstate, err := requestIpAddressAndGetState(t, req)
|
||||
actualstate, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatal("Expected IP retrieval to be nil")
|
||||
}
|
||||
|
@ -177,9 +150,11 @@ func TestIPAMGetAvailableIPConfig(t *testing.T) {
|
|||
desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, types.Assigned, 0)
|
||||
desiredState.PodInfo = testPod1Info
|
||||
|
||||
if reflect.DeepEqual(desiredState, actualstate) != true {
|
||||
t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate)
|
||||
}
|
||||
assert.Equal(t, desiredState.GetState(), actualstate.GetState())
|
||||
assert.Equal(t, desiredState.ID, actualstate.ID)
|
||||
assert.Equal(t, desiredState.IPAddress, actualstate.IPAddress)
|
||||
assert.Equal(t, desiredState.NCID, actualstate.NCID)
|
||||
assert.Equal(t, desiredState.PodInfo, actualstate.PodInfo)
|
||||
}
|
||||
|
||||
// First IP is already assigned to a pod, want second IP
|
||||
|
@ -207,16 +182,18 @@ func TestIPAMGetNextAvailableIPConfig(t *testing.T) {
|
|||
b, _ := testPod2Info.OrchestratorContext()
|
||||
req.OrchestratorContext = b
|
||||
|
||||
actualstate, err := requestIpAddressAndGetState(t, req)
|
||||
actualstate, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected IP retrieval to be nil: %+v", err)
|
||||
}
|
||||
// want second available Pod IP State as first has been assigned
|
||||
desiredState, _ := NewPodStateWithOrchestratorContext(testIP2, testPod2GUID, testNCID, types.Assigned, 24, 0, testPod2Info)
|
||||
|
||||
if reflect.DeepEqual(desiredState, actualstate) != true {
|
||||
t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate)
|
||||
}
|
||||
assert.Equal(t, desiredState.GetState(), actualstate.GetState())
|
||||
assert.Equal(t, desiredState.ID, actualstate.ID)
|
||||
assert.Equal(t, desiredState.IPAddress, actualstate.IPAddress)
|
||||
assert.Equal(t, desiredState.NCID, actualstate.NCID)
|
||||
assert.Equal(t, desiredState.PodInfo, actualstate.PodInfo)
|
||||
}
|
||||
|
||||
func TestIPAMGetAlreadyAssignedIPConfigForSamePod(t *testing.T) {
|
||||
|
@ -239,16 +216,18 @@ func TestIPAMGetAlreadyAssignedIPConfigForSamePod(t *testing.T) {
|
|||
b, _ := testPod1Info.OrchestratorContext()
|
||||
req.OrchestratorContext = b
|
||||
|
||||
actualstate, err := requestIpAddressAndGetState(t, req)
|
||||
actualstate, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected not error: %+v", err)
|
||||
}
|
||||
|
||||
desiredState, _ := NewPodStateWithOrchestratorContext(testIP1, testPod1GUID, testNCID, types.Assigned, 24, 0, testPod1Info)
|
||||
|
||||
if reflect.DeepEqual(desiredState, actualstate) != true {
|
||||
t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate)
|
||||
}
|
||||
assert.Equal(t, desiredState.GetState(), actualstate.GetState())
|
||||
assert.Equal(t, desiredState.ID, actualstate.ID)
|
||||
assert.Equal(t, desiredState.IPAddress, actualstate.IPAddress)
|
||||
assert.Equal(t, desiredState.NCID, actualstate.NCID)
|
||||
assert.Equal(t, desiredState.PodInfo, actualstate.PodInfo)
|
||||
}
|
||||
|
||||
func TestIPAMAttemptToRequestIPNotFoundInPool(t *testing.T) {
|
||||
|
@ -273,7 +252,7 @@ func TestIPAMAttemptToRequestIPNotFoundInPool(t *testing.T) {
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = testIP2
|
||||
|
||||
_, err = requestIpAddressAndGetState(t, req)
|
||||
_, err = requestIPAddressAndGetState(t, req)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected to fail as IP not found in pool")
|
||||
}
|
||||
|
@ -301,7 +280,7 @@ func TestIPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T) {
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = testIP1
|
||||
|
||||
actualstate, err := requestIpAddressAndGetState(t, req)
|
||||
actualstate, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected IP retrieval to be nil: %+v", err)
|
||||
}
|
||||
|
@ -309,9 +288,11 @@ func TestIPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T) {
|
|||
desiredState := NewPodState(testIP1, 24, testPod1GUID, testNCID, types.Assigned, 0)
|
||||
desiredState.PodInfo = testPod1Info
|
||||
|
||||
if reflect.DeepEqual(desiredState, actualstate) != true {
|
||||
t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", desiredState, actualstate)
|
||||
}
|
||||
assert.Equal(t, desiredState.GetState(), actualstate.GetState())
|
||||
assert.Equal(t, desiredState.ID, actualstate.ID)
|
||||
assert.Equal(t, desiredState.IPAddress, actualstate.IPAddress)
|
||||
assert.Equal(t, desiredState.NCID, actualstate.NCID)
|
||||
assert.Equal(t, desiredState.PodInfo, actualstate.PodInfo)
|
||||
}
|
||||
|
||||
func TestIPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t *testing.T) {
|
||||
|
@ -336,7 +317,7 @@ func TestIPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t *testing.T)
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = testIP1
|
||||
|
||||
_, err = requestIpAddressAndGetState(t, req)
|
||||
_, err = requestIPAddressAndGetState(t, req)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected failure requesting already assigned IP: %+v", err)
|
||||
}
|
||||
|
@ -363,7 +344,7 @@ func TestIPAMFailToGetIPWhenAllIPsAreAssigned(t *testing.T) {
|
|||
b, _ := testPod3Info.OrchestratorContext()
|
||||
req.OrchestratorContext = b
|
||||
|
||||
_, err = requestIpAddressAndGetState(t, req)
|
||||
_, err = requestIPAddressAndGetState(t, req)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected failure requesting IP when there are no more IPs: %+v", err)
|
||||
}
|
||||
|
@ -398,7 +379,7 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) {
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = desiredIpAddress
|
||||
|
||||
_, err = requestIpAddressAndGetState(t, req)
|
||||
_, err = requestIPAddressAndGetState(t, req)
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure requesting IP when there are no more IPs")
|
||||
}
|
||||
|
@ -418,7 +399,7 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) {
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = desiredIpAddress
|
||||
|
||||
actualstate, err := requestIpAddressAndGetState(t, req)
|
||||
actualstate, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected IP retrieval to be nil: %+v", err)
|
||||
}
|
||||
|
@ -428,9 +409,11 @@ func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) {
|
|||
desiredState.IPAddress = desiredIpAddress
|
||||
desiredState.PodInfo = testPod2Info
|
||||
|
||||
if reflect.DeepEqual(desiredState, actualstate) != true {
|
||||
t.Fatalf("Desired state not matching actual state, expected: %+v, actual: %+v", state1, actualstate)
|
||||
}
|
||||
assert.Equal(t, desiredState.GetState(), actualstate.GetState())
|
||||
assert.Equal(t, desiredState.ID, actualstate.ID)
|
||||
assert.Equal(t, desiredState.IPAddress, actualstate.IPAddress)
|
||||
assert.Equal(t, desiredState.NCID, actualstate.NCID)
|
||||
assert.Equal(t, desiredState.PodInfo, actualstate.PodInfo)
|
||||
}
|
||||
|
||||
func TestIPAMReleaseIPIdempotency(t *testing.T) {
|
||||
|
@ -513,7 +496,7 @@ func TestAvailableIPConfigs(t *testing.T) {
|
|||
req.OrchestratorContext = b
|
||||
req.DesiredIPAddress = state1.IPAddress
|
||||
|
||||
_, err := requestIpAddressAndGetState(t, req)
|
||||
_, err := requestIPAddressAndGetState(t, req)
|
||||
if err != nil {
|
||||
t.Fatal("Expected IP retrieval to be nil")
|
||||
}
|
||||
|
@ -534,18 +517,18 @@ func validateIpState(t *testing.T, actualIps []cns.IPConfigurationStatus, expect
|
|||
t.Fatalf("Actual and expected count doesnt match, expected %d, actual %d", len(actualIps), len(expectedList))
|
||||
}
|
||||
|
||||
for _, actualIp := range actualIps {
|
||||
var expectedIp cns.IPConfigurationStatus
|
||||
for _, actualIP := range actualIps { //nolint:gocritic // ignore copy
|
||||
var expectedIP cns.IPConfigurationStatus
|
||||
var found bool
|
||||
for _, expectedIp = range expectedList {
|
||||
if reflect.DeepEqual(actualIp, expectedIp) == true {
|
||||
for _, expectedIP = range expectedList { //nolint:gocritic // ignore copy
|
||||
if expectedIP.Equals(actualIP) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatalf("Actual and expected list doesnt match actual: %+v, expected: %+v", actualIp, expectedIp)
|
||||
t.Fatalf("Actual and expected list doesnt match actual: %+v, expected: %+v", actualIP, expectedIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -635,10 +618,10 @@ func TestIPAMMarkIPAsPendingWithPendingProgrammingIPs(t *testing.T) {
|
|||
}
|
||||
// Check returning released IPs are from pod 1 and 3
|
||||
if _, exists := ips[testPod1GUID]; !exists {
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %s", err, ips)
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %v", err, ips)
|
||||
}
|
||||
if _, exists := ips[testPod3GUID]; !exists {
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %s", err, ips)
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %v", err, ips)
|
||||
}
|
||||
|
||||
pendingRelease := svc.GetPendingReleaseIPConfigs()
|
||||
|
@ -670,10 +653,10 @@ func TestIPAMMarkIPAsPendingWithPendingProgrammingIPs(t *testing.T) {
|
|||
}
|
||||
// Make sure newly released IPs are from pod 2 and pod 4
|
||||
if _, exists := ips[testPod2GUID]; !exists {
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %s", err, ips)
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %v", err, ips)
|
||||
}
|
||||
if _, exists := ips[testPod4GUID]; !exists {
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %s", err, ips)
|
||||
t.Fatalf("Expected ID not marked as pending: %+v, ips is %v", err, ips)
|
||||
}
|
||||
|
||||
// Get all pending release IPs and check total number is 4
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-container-networking/cns"
|
||||
"github.com/Azure/azure-container-networking/cns/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
)
|
||||
|
@ -31,10 +33,21 @@ var ipAssignmentLatency = prometheus.NewHistogram(
|
|||
},
|
||||
)
|
||||
|
||||
var ipConfigStatusStateTransitionTime = prometheus.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "ipconfigstatus_state_transition",
|
||||
Help: "Time spent by the IP Configuration Status in each state transition",
|
||||
//nolint:gomnd // default bucket consts
|
||||
Buckets: prometheus.ExponentialBuckets(0.001, 2, 15), // 1 ms to ~16 seconds
|
||||
},
|
||||
[]string{"previous_state", "next_state"},
|
||||
)
|
||||
|
||||
func init() {
|
||||
metrics.Registry.MustRegister(
|
||||
httpRequestLatency,
|
||||
ipAssignmentLatency,
|
||||
ipConfigStatusStateTransitionTime,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -47,3 +60,11 @@ func newHandlerFuncWithHistogram(handler http.HandlerFunc, histogram *prometheus
|
|||
handler(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func stateTransitionMiddleware(i *cns.IPConfigurationStatus, s types.IPState) {
|
||||
// if no state transition has been recorded yet, don't collect any metric
|
||||
if i.LastStateTransition.IsZero() {
|
||||
return
|
||||
}
|
||||
ipConfigStatusStateTransitionTime.WithLabelValues(string(i.GetState()), string(s)).Observe(time.Since(i.LastStateTransition).Seconds())
|
||||
}
|
||||
|
|
|
@ -225,7 +225,7 @@ func (service *HTTPRestService) updateIPConfigsStateUntransacted(
|
|||
ipConfigStatus, exists := service.PodIPConfigState[ipID]
|
||||
if exists {
|
||||
// pod ip exists, validate if state is not assigned, else fail
|
||||
if ipConfigStatus.State == types.Assigned {
|
||||
if ipConfigStatus.GetState() == types.Assigned {
|
||||
errMsg := fmt.Sprintf("Failed to delete an Assigned IP %v", ipConfigStatus)
|
||||
return types.InconsistentIPConfigState, errMsg
|
||||
}
|
||||
|
@ -288,9 +288,10 @@ func (service *HTTPRestService) addIPConfigStateUntransacted(ncID string, hostVe
|
|||
NCID: ncID,
|
||||
ID: ipID,
|
||||
IPAddress: ipconfig.IPAddress,
|
||||
State: newIPCNSStatus,
|
||||
PodInfo: nil,
|
||||
}
|
||||
ipconfigStatus.WithStateMiddleware(stateTransitionMiddleware)
|
||||
ipconfigStatus.SetState(newIPCNSStatus)
|
||||
logger.Printf("[Azure-Cns] Add IP %s as %s", ipconfig.IPAddress, newIPCNSStatus)
|
||||
|
||||
service.PodIPConfigState[ipID] = ipconfigStatus
|
||||
|
@ -321,7 +322,7 @@ func (service *HTTPRestService) removeToBeDeletedIPStateUntransacted(
|
|||
ipConfigStatus, exists := service.PodIPConfigState[ipID]
|
||||
if exists {
|
||||
// pod ip exists, validate if state is not assigned, else fail
|
||||
if ipConfigStatus.State == types.Assigned {
|
||||
if ipConfigStatus.GetState() == types.Assigned {
|
||||
errMsg := fmt.Sprintf("Failed to delete an Assigned IP %v", ipConfigStatus)
|
||||
return types.InconsistentIPConfigState, errMsg
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче