test: [WIN-NPM] dataplane test framework (#1652)
* wip with StrictlyHasSetPolicies approach * better approaching of getting all set policies * wip for rigorous win dp UTs * marshal setpolicies in hns mock and dont short circuit in UTs * policy stuff and update test cases * marshal ACLs in hns mock * more UTs and minor refinements * option to apply dp or not * address cmp.Equal and t.Helper comments * dpEvent returns error and better defined concurrency * remove unnecessary logic in concurrent test code * approach #3 emulating cyclonus * namespace method for podmetadata * refactor Action structure and TestCase wait group behavior * hnsactions and renaming a file * refactor to Serial and ThreadedTestCase structs, and move files to dp pkg * hns latency hard coded to be the same for all threaded test cases * fix build error after rebasing * export fake hns network id * address comments on multierr and terminology * add comment about pod metadata in controller * pod update and delete actions * move ApplyDPAction to top * namespace actions and rename some fields of UpdatePod * adding code comments * reconcile action * fix bug in key-val ipsets * implement all previous test cases * fix incorrect error wrapping in dataplane.go * multi-job tests are working. updated terminology from routine to job * MultiErrManager instead of dependency for multierr * return to the channel approach for multierr, now using FailNow instead of asserting on channel length * fix some lints * fix more lints
This commit is contained in:
Родитель
b231f0f2e3
Коммит
e3ffab843d
|
@ -9,6 +9,8 @@ import (
|
|||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
|
||||
const FakeHNSNetworkID = "1234"
|
||||
|
||||
type IOShim struct {
|
||||
Exec utilexec.Interface
|
||||
Hns hnswrapper.HnsV2WrapperInterface
|
||||
|
@ -24,7 +26,7 @@ func NewIOShim() *IOShim {
|
|||
func NewMockIOShim(calls []testutils.TestCmd) *IOShim {
|
||||
hns := hnswrapper.NewHnsv2wrapperFake()
|
||||
network := &hcn.HostComputeNetwork{
|
||||
Id: "1234",
|
||||
Id: FakeHNSNetworkID,
|
||||
Name: "azure",
|
||||
}
|
||||
|
||||
|
|
|
@ -317,10 +317,13 @@ func (f Hnsv2wrapperFake) GetEndpointByName(endpointName string) (*hcn.HostCompu
|
|||
}
|
||||
|
||||
type FakeHNSCache struct {
|
||||
networks map[string]*FakeHostComputeNetwork
|
||||
// networks maps network name to network object
|
||||
networks map[string]*FakeHostComputeNetwork
|
||||
// endpoints maps endpoint ID to endpoint object
|
||||
endpoints map[string]*FakeHostComputeEndpoint
|
||||
}
|
||||
|
||||
// SetPolicy returns the first SetPolicy found with this ID in any network.
|
||||
func (fCache FakeHNSCache) SetPolicy(setID string) *hcn.SetPolicySetting {
|
||||
for _, network := range fCache.networks {
|
||||
for _, policy := range network.Policies {
|
||||
|
@ -332,6 +335,35 @@ func (fCache FakeHNSCache) SetPolicy(setID string) *hcn.SetPolicySetting {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (fCache FakeHNSCache) PrettyString() string {
|
||||
networkStrings := make([]string, 0, len(fCache.networks))
|
||||
for _, network := range fCache.networks {
|
||||
networkStrings = append(networkStrings, fmt.Sprintf("[%+v]", network.PrettyString()))
|
||||
}
|
||||
|
||||
endpointStrings := make([]string, 0, len(fCache.endpoints))
|
||||
for _, endpoint := range fCache.endpoints {
|
||||
endpointStrings = append(endpointStrings, fmt.Sprintf("[%+v]", endpoint.PrettyString()))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("networks: %s\nendpoints: %s", strings.Join(networkStrings, ","), strings.Join(endpointStrings, ","))
|
||||
}
|
||||
|
||||
// AllSetPolicies returns all SetPolicies in a given network as a map of SetPolicy ID to SetPolicy object.
|
||||
func (fCache FakeHNSCache) AllSetPolicies(networkID string) map[string]*hcn.SetPolicySetting {
|
||||
setPolicies := make(map[string]*hcn.SetPolicySetting)
|
||||
for _, network := range fCache.networks {
|
||||
if network.ID == networkID {
|
||||
for _, setPolicy := range network.Policies {
|
||||
setPolicies[setPolicy.Id] = setPolicy
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return setPolicies
|
||||
}
|
||||
|
||||
// ACLPolicies returns a map of the inputed Endpoint IDs to Policies with the given policyID.
|
||||
func (fCache FakeHNSCache) ACLPolicies(epList map[string]string, policyID string) (map[string][]*FakeEndpointPolicy, error) {
|
||||
aclPols := make(map[string][]*FakeEndpointPolicy)
|
||||
for ip, epID := range epList {
|
||||
|
@ -354,6 +386,7 @@ func (fCache FakeHNSCache) ACLPolicies(epList map[string]string, policyID string
|
|||
return aclPols, nil
|
||||
}
|
||||
|
||||
// GetAllACLs maps all Endpoint IDs to ACLs
|
||||
func (fCache FakeHNSCache) GetAllACLs() map[string][]*FakeEndpointPolicy {
|
||||
aclPols := make(map[string][]*FakeEndpointPolicy)
|
||||
for _, ep := range fCache.endpoints {
|
||||
|
@ -362,9 +395,20 @@ func (fCache FakeHNSCache) GetAllACLs() map[string][]*FakeEndpointPolicy {
|
|||
return aclPols
|
||||
}
|
||||
|
||||
// EndpointIP returns the Endpoint's IP or an empty string if the Endpoint doesn't exist.
|
||||
func (fCache FakeHNSCache) EndpointIP(id string) string {
|
||||
for _, ep := range fCache.endpoints {
|
||||
if ep.ID == id {
|
||||
return ep.IPConfiguration
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FakeHostComputeNetwork struct {
|
||||
ID string
|
||||
Name string
|
||||
ID string
|
||||
Name string
|
||||
// Policies maps SetPolicy ID to SetPolicy object
|
||||
Policies map[string]*hcn.SetPolicySetting
|
||||
}
|
||||
|
||||
|
@ -376,10 +420,33 @@ func NewFakeHostComputeNetwork(network *hcn.HostComputeNetwork) *FakeHostCompute
|
|||
}
|
||||
}
|
||||
|
||||
func (fNetwork *FakeHostComputeNetwork) PrettyString() string {
|
||||
setPolicyStrings := make([]string, 0, len(fNetwork.Policies))
|
||||
for _, setPolicy := range fNetwork.Policies {
|
||||
setPolicyStrings = append(setPolicyStrings, fmt.Sprintf("[%+v]", setPolicy))
|
||||
}
|
||||
return fmt.Sprintf("ID: %s, Name: %s, SetPolicies: [%s]", fNetwork.ID, fNetwork.Name, strings.Join(setPolicyStrings, ","))
|
||||
}
|
||||
|
||||
func (fNetwork *FakeHostComputeNetwork) GetHCNObj() *hcn.HostComputeNetwork {
|
||||
setPolicies := make([]hcn.NetworkPolicy, 0)
|
||||
for _, setPolicy := range fNetwork.Policies {
|
||||
rawSettings, err := json.Marshal(setPolicy)
|
||||
if err != nil {
|
||||
fmt.Printf("FakeHostComputeNetwork: error marshalling SetPolicy: %+v. err: %s\n", setPolicy, err.Error())
|
||||
continue
|
||||
}
|
||||
policy := hcn.NetworkPolicy{
|
||||
Type: hcn.SetPolicy,
|
||||
Settings: rawSettings,
|
||||
}
|
||||
setPolicies = append(setPolicies, policy)
|
||||
}
|
||||
|
||||
return &hcn.HostComputeNetwork{
|
||||
Id: fNetwork.ID,
|
||||
Name: fNetwork.Name,
|
||||
Id: fNetwork.ID,
|
||||
Name: fNetwork.Name,
|
||||
Policies: setPolicies,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,29 +471,41 @@ func NewFakeHostComputeEndpoint(endpoint *hcn.HostComputeEndpoint) *FakeHostComp
|
|||
}
|
||||
}
|
||||
|
||||
func (fEndpoint *FakeHostComputeEndpoint) PrettyString() string {
|
||||
aclStrings := make([]string, 0, len(fEndpoint.Policies))
|
||||
for _, acl := range fEndpoint.Policies {
|
||||
aclStrings = append(aclStrings, fmt.Sprintf("[%+v]", acl))
|
||||
}
|
||||
return fmt.Sprintf("ID: %s, Name: %s, IP: %s, ACLs: [%s]",
|
||||
fEndpoint.ID, fEndpoint.Name, fEndpoint.IPConfiguration, strings.Join(aclStrings, ","))
|
||||
}
|
||||
|
||||
func (fEndpoint *FakeHostComputeEndpoint) GetHCNObj() *hcn.HostComputeEndpoint {
|
||||
// NOTE: not including other policy types like perhaps SetPolicies
|
||||
hcnEndpoint := &hcn.HostComputeEndpoint{
|
||||
acls := make([]hcn.EndpointPolicy, 0)
|
||||
for _, acl := range fEndpoint.Policies {
|
||||
rawSettings, err := json.Marshal(acl)
|
||||
if err != nil {
|
||||
fmt.Printf("FakeHostComputeEndpoint: error marshalling ACL: %+v. err: %s\n", acl, err.Error())
|
||||
continue
|
||||
}
|
||||
policy := hcn.EndpointPolicy{
|
||||
Type: hcn.ACL,
|
||||
Settings: rawSettings,
|
||||
}
|
||||
acls = append(acls, policy)
|
||||
}
|
||||
|
||||
return &hcn.HostComputeEndpoint{
|
||||
Id: fEndpoint.ID,
|
||||
Name: fEndpoint.Name,
|
||||
HostComputeNetwork: fEndpoint.HostComputeNetwork,
|
||||
Policies: make([]hcn.EndpointPolicy, 0),
|
||||
IpConfigurations: []hcn.IpConfig{
|
||||
{
|
||||
IpAddress: fEndpoint.IPConfiguration,
|
||||
},
|
||||
},
|
||||
Policies: acls,
|
||||
}
|
||||
|
||||
for _, fakeEndpointPol := range fEndpoint.Policies {
|
||||
rawJSON, err := json.Marshal(fakeEndpointPol)
|
||||
if err != nil {
|
||||
fmt.Printf("FAILURE marshalling fake endpoint policy: %s\n", err.Error())
|
||||
} else {
|
||||
hcnPolicy := hcn.EndpointPolicy{
|
||||
Type: hcn.ACL,
|
||||
Settings: rawJSON,
|
||||
}
|
||||
hcnEndpoint.Policies = append(hcnEndpoint.Policies, hcnPolicy)
|
||||
}
|
||||
}
|
||||
|
||||
return hcnEndpoint
|
||||
}
|
||||
|
||||
func (fEndpoint *FakeHostComputeEndpoint) RemovePolicy(toRemovePol *FakeEndpointPolicy) error {
|
||||
|
|
|
@ -573,6 +573,8 @@ func (c *PodController) cleanUpDeletedPod(cachedNpmPodKey string) error {
|
|||
func (c *PodController) manageNamedPortIpsets(portList []corev1.ContainerPort, podKey,
|
||||
podIP, nodeName string, namedPortOperation NamedPortOperation) error {
|
||||
if util.IsWindowsDP() {
|
||||
// NOTE: if we support namedport operations, need to be careful of implications of including the node name in the pod metadata below
|
||||
// since we say the node name is "" in cleanUpDeletedPod
|
||||
klog.Warningf("Windows Dataplane does not support NamedPort operations. Operation: %s portList is %+v", namedPortOperation, portList)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,448 @@
|
|||
package dataplane
|
||||
|
||||
import (
|
||||
"github.com/Azure/azure-container-networking/network/hnswrapper"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/policies"
|
||||
dptestutils "github.com/Azure/azure-container-networking/npm/pkg/dataplane/testutils"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// tags
|
||||
const (
|
||||
podCrudTag Tag = "pod-crud"
|
||||
nsCrudTag Tag = "namespace-crud"
|
||||
netpolCrudTag Tag = "netpol-crud"
|
||||
)
|
||||
|
||||
const (
|
||||
thisNode = "this-node"
|
||||
otherNode = "other-node"
|
||||
|
||||
ip1 = "10.0.0.1"
|
||||
ip2 = "10.0.0.2"
|
||||
|
||||
endpoint1 = "test1"
|
||||
endpoint2 = "test2"
|
||||
)
|
||||
|
||||
// IPSet constants
|
||||
var (
|
||||
podK1Set = ipsets.NewIPSetMetadata("k1", ipsets.KeyLabelOfPod)
|
||||
podK1V1Set = ipsets.NewIPSetMetadata("k1:v1", ipsets.KeyValueLabelOfPod)
|
||||
podK2Set = ipsets.NewIPSetMetadata("k2", ipsets.KeyLabelOfPod)
|
||||
podK2V2Set = ipsets.NewIPSetMetadata("k2:v2", ipsets.KeyValueLabelOfPod)
|
||||
|
||||
// emptySet is a member of a list if enabled in the dp Config
|
||||
// in Windows, this Config option is actually forced to be enabled in NewDataPlane()
|
||||
emptySet = ipsets.NewIPSetMetadata("emptyhashset", ipsets.EmptyHashSet)
|
||||
allNamespaces = ipsets.NewIPSetMetadata("all-namespaces", ipsets.KeyLabelOfNamespace)
|
||||
nsXSet = ipsets.NewIPSetMetadata("x", ipsets.Namespace)
|
||||
nsYSet = ipsets.NewIPSetMetadata("y", ipsets.Namespace)
|
||||
|
||||
nsK1Set = ipsets.NewIPSetMetadata("k1", ipsets.KeyLabelOfNamespace)
|
||||
nsK1V1Set = ipsets.NewIPSetMetadata("k1:v1", ipsets.KeyValueLabelOfNamespace)
|
||||
nsK2Set = ipsets.NewIPSetMetadata("k2", ipsets.KeyLabelOfNamespace)
|
||||
nsK2V2Set = ipsets.NewIPSetMetadata("k2:v2", ipsets.KeyValueLabelOfNamespace)
|
||||
)
|
||||
|
||||
// DP Configs
|
||||
var (
|
||||
defaultWindowsDPCfg = &Config{
|
||||
IPSetManagerCfg: &ipsets.IPSetManagerCfg{
|
||||
IPSetMode: ipsets.ApplyAllIPSets,
|
||||
AddEmptySetToLists: true,
|
||||
},
|
||||
PolicyManagerCfg: &policies.PolicyManagerCfg{
|
||||
PolicyMode: policies.IPSetPolicyMode,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func policyXBaseOnK1V1() *networkingv1.NetworkPolicy {
|
||||
return &networkingv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "base",
|
||||
Namespace: "x",
|
||||
},
|
||||
Spec: networkingv1.NetworkPolicySpec{
|
||||
PodSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"k1": "v1",
|
||||
},
|
||||
},
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||
{},
|
||||
},
|
||||
Egress: []networkingv1.NetworkPolicyEgressRule{
|
||||
{},
|
||||
},
|
||||
PolicyTypes: []networkingv1.PolicyType{
|
||||
networkingv1.PolicyTypeIngress,
|
||||
networkingv1.PolicyTypeEgress,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getAllSerialTests() []*SerialTestCase {
|
||||
return []*SerialTestCase{
|
||||
{
|
||||
Description: "pod created",
|
||||
Actions: []*Action{
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(podK1Set, ip1),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "pod created, then pod deleted",
|
||||
Actions: []*Action{
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
DeleteEndpoint(endpoint1),
|
||||
DeletePod("x", "a", ip1, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet),
|
||||
dptestutils.SetPolicy(podK1Set),
|
||||
dptestutils.SetPolicy(podK1V1Set),
|
||||
},
|
||||
ExpectedEnpdointACLs: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "pod created, then pod deleted, then ipsets garbage collected",
|
||||
Actions: []*Action{
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
DeleteEndpoint(endpoint1),
|
||||
DeletePod("x", "a", ip1, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
ReconcileDP(),
|
||||
ApplyDP(),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet),
|
||||
},
|
||||
ExpectedEnpdointACLs: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "policy created with no pods",
|
||||
Actions: []*Action{
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
// will not be an all-namespaces IPSet unless there's a Pod/Namespace event
|
||||
dptestutils.SetPolicy(nsXSet),
|
||||
// Policies do not create the KeyLabelOfPod type IPSet if the selector has a key-value requirement
|
||||
dptestutils.SetPolicy(podK1V1Set),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "pod created on node, then relevant policy created",
|
||||
Actions: []*Action{
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
// will apply dirty ipsets from CreatePod
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(podK1Set, ip1),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "In",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "Out",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "pod created on node, then relevant policy created, then policy deleted",
|
||||
Actions: []*Action{
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
// will apply dirty ipsets from CreatePod
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
DeletePolicyByObject(policyXBaseOnK1V1()),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(podK1Set, ip1),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "pod created off node (no local endpoint), then relevant policy created",
|
||||
Actions: []*Action{
|
||||
CreatePod("x", "a", ip1, otherNode, map[string]string{"k1": "v1"}),
|
||||
// will apply dirty ipsets from CreatePod
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(podK1Set, ip1),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "policy created, then pod created which satisfies policy",
|
||||
Actions: []*Action{
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(podK1Set, ip1),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "In",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "Out",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Description: "policy created, then pod created which satisfies policy, then pod relabeled and no longer satisfies policy",
|
||||
Actions: []*Action{
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
CreateEndpoint(endpoint1, ip1),
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
UpdatePodLabels("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}, map[string]string{"k2": "v2"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: nil,
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
// old labels (not yet garbage collected)
|
||||
dptestutils.SetPolicy(podK1Set),
|
||||
dptestutils.SetPolicy(podK1V1Set),
|
||||
// new labels
|
||||
dptestutils.SetPolicy(podK2Set, ip1),
|
||||
dptestutils.SetPolicy(podK2V2Set, ip1),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getAllMultiJobTests() []*MultiJobTestCase {
|
||||
return []*MultiJobTestCase{
|
||||
{
|
||||
Description: "create namespaces, pods, and a policy which applies to a pod",
|
||||
Jobs: map[string][]*Action{
|
||||
"namespace_controller": {
|
||||
CreateNamespace("x", map[string]string{"k1": "v1"}),
|
||||
CreateNamespace("y", map[string]string{"k2": "v2"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
"pod_controller": {
|
||||
CreatePod("x", "a", ip1, thisNode, map[string]string{"k1": "v1"}),
|
||||
CreatePod("y", "a", ip2, otherNode, map[string]string{"k1": "v1"}),
|
||||
ApplyDP(),
|
||||
},
|
||||
"policy_controller": {
|
||||
UpdatePolicy(policyXBaseOnK1V1()),
|
||||
},
|
||||
},
|
||||
TestCaseMetadata: &TestCaseMetadata{
|
||||
Tags: []Tag{
|
||||
nsCrudTag,
|
||||
podCrudTag,
|
||||
netpolCrudTag,
|
||||
},
|
||||
DpCfg: defaultWindowsDPCfg,
|
||||
InitialEndpoints: []*hcn.HostComputeEndpoint{
|
||||
dptestutils.Endpoint(endpoint1, ip1),
|
||||
dptestutils.Endpoint(endpoint2, ip2),
|
||||
},
|
||||
ExpectedSetPolicies: []*hcn.SetPolicySetting{
|
||||
dptestutils.SetPolicy(emptySet),
|
||||
dptestutils.SetPolicy(allNamespaces, emptySet.GetHashedName(), nsXSet.GetHashedName(), nsYSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsXSet, ip1),
|
||||
dptestutils.SetPolicy(nsYSet, ip2),
|
||||
dptestutils.SetPolicy(nsK1Set, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsK1V1Set, emptySet.GetHashedName(), nsXSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsK2Set, emptySet.GetHashedName(), nsYSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(nsK2V2Set, emptySet.GetHashedName(), nsYSet.GetHashedName()),
|
||||
dptestutils.SetPolicy(podK1Set, ip1, ip2),
|
||||
dptestutils.SetPolicy(podK1V1Set, ip1, ip2),
|
||||
},
|
||||
ExpectedEnpdointACLs: map[string][]*hnswrapper.FakeEndpointPolicy{
|
||||
endpoint1: {
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "In",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
{
|
||||
ID: "azure-acl-x-base",
|
||||
Protocols: "",
|
||||
Action: "Allow",
|
||||
Direction: "Out",
|
||||
LocalAddresses: "",
|
||||
RemoteAddresses: "",
|
||||
LocalPorts: "",
|
||||
RemotePorts: "",
|
||||
Priority: 222,
|
||||
},
|
||||
},
|
||||
endpoint2: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -240,7 +240,7 @@ func (dp *DataPlane) ApplyDataPlane() error {
|
|||
delete(dp.updatePodCache.cache, podKey)
|
||||
}
|
||||
if aggregateErr != nil {
|
||||
return fmt.Errorf("[DataPlane] error while updating pods: %w", err)
|
||||
return fmt.Errorf("[DataPlane] error while updating pods: %w", aggregateErr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package dataplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-container-networking/common"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
|
||||
dptestutils "github.com/Azure/azure-container-networking/npm/pkg/dataplane/testutils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHNSLatency = time.Duration(0)
|
||||
threadedHNSLatency = time.Duration(1 * time.Second)
|
||||
)
|
||||
|
||||
func TestAllSerialCases(t *testing.T) {
|
||||
tests := getAllSerialTests()
|
||||
for i, tt := range tests {
|
||||
i := i
|
||||
tt := tt
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
t.Logf("beginning test #%d. Description: [%s]. Tags: %+v", i, tt.Description, tt.Tags)
|
||||
|
||||
hns := ipsets.GetHNSFake(t)
|
||||
hns.Delay = defaultHNSLatency
|
||||
io := common.NewMockIOShimWithFakeHNS(hns)
|
||||
for _, ep := range tt.InitialEndpoints {
|
||||
_, err := hns.CreateEndpoint(ep)
|
||||
require.Nil(t, err, "failed to create initial endpoint %+v", ep)
|
||||
}
|
||||
|
||||
dp, err := NewDataPlane(thisNode, io, tt.DpCfg, nil)
|
||||
require.NoError(t, err, "failed to initialize dp")
|
||||
|
||||
for j, a := range tt.Actions {
|
||||
var err error
|
||||
if a.HNSAction != nil {
|
||||
err = a.HNSAction.Do(hns)
|
||||
} else if a.DPAction != nil {
|
||||
err = a.DPAction.Do(dp)
|
||||
}
|
||||
|
||||
require.Nil(t, err, "failed to run action %d", j)
|
||||
}
|
||||
|
||||
dptestutils.VerifyHNSCache(t, hns, tt.ExpectedSetPolicies, tt.ExpectedEnpdointACLs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllMultiJobCases(t *testing.T) {
|
||||
tests := getAllMultiJobTests()
|
||||
for i, tt := range tests {
|
||||
i := i
|
||||
tt := tt
|
||||
t.Run(tt.Description, func(t *testing.T) {
|
||||
t.Logf("beginning test #%d. Description: [%s]. Tags: %+v", i, tt.Description, tt.Tags)
|
||||
|
||||
hns := ipsets.GetHNSFake(t)
|
||||
hns.Delay = threadedHNSLatency
|
||||
io := common.NewMockIOShimWithFakeHNS(hns)
|
||||
for _, ep := range tt.InitialEndpoints {
|
||||
_, err := hns.CreateEndpoint(ep)
|
||||
require.Nil(t, err, "failed to create initial endpoint %+v", ep)
|
||||
}
|
||||
|
||||
// the dp is necessary for NPM tests
|
||||
dp, err := NewDataPlane(thisNode, io, tt.DpCfg, nil)
|
||||
require.NoError(t, err, "failed to initialize dp")
|
||||
|
||||
backgroundErrors := make(chan error, len(tt.Jobs))
|
||||
wg := new(sync.WaitGroup)
|
||||
wg.Add(len(tt.Jobs))
|
||||
for jobName, job := range tt.Jobs {
|
||||
jobName := jobName
|
||||
job := job
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for k, a := range job {
|
||||
var err error
|
||||
if a.HNSAction != nil {
|
||||
err = a.HNSAction.Do(hns)
|
||||
} else if a.DPAction != nil {
|
||||
err = a.DPAction.Do(dp)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
backgroundErrors <- errors.Wrapf(err, "failed to run action %d in job %s", k, jobName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(backgroundErrors)
|
||||
if len(backgroundErrors) > 0 {
|
||||
errStrings := make([]string, 0)
|
||||
for err := range backgroundErrors {
|
||||
errStrings = append(errStrings, fmt.Sprintf("[%s]", err.Error()))
|
||||
}
|
||||
require.FailNow(t, "encountered errors in multi-job test: %+v", errStrings)
|
||||
}
|
||||
|
||||
dptestutils.VerifyHNSCache(t, hns, tt.ExpectedSetPolicies, tt.ExpectedEnpdointACLs)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,16 +3,17 @@ package ipsets
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-container-networking/common"
|
||||
"github.com/Azure/azure-container-networking/network/hnswrapper"
|
||||
testutils "github.com/Azure/azure-container-networking/test/utils"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func GetHNSFake(t *testing.T) *hnswrapper.Hnsv2wrapperFake {
|
||||
hns := hnswrapper.NewHnsv2wrapperFake()
|
||||
network := &hcn.HostComputeNetwork{
|
||||
Id: "1234",
|
||||
Id: common.FakeHNSNetworkID,
|
||||
Name: "azure",
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
package dptestutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-container-networking/common"
|
||||
"github.com/Azure/azure-container-networking/network/hnswrapper"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
func PrefixNames(sets []*ipsets.IPSetMetadata) []string {
|
||||
a := make([]string, len(sets))
|
||||
for k, s := range sets {
|
||||
a[k] = s.GetPrefixName()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func Endpoint(epID, ip string) *hcn.HostComputeEndpoint {
|
||||
return &hcn.HostComputeEndpoint{
|
||||
Id: epID,
|
||||
Name: epID,
|
||||
HostComputeNetwork: common.FakeHNSNetworkID,
|
||||
IpConfigurations: []hcn.IpConfig{
|
||||
{
|
||||
IpAddress: ip,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func SetPolicy(setMetadata *ipsets.IPSetMetadata, members ...string) *hcn.SetPolicySetting {
|
||||
pType := hcn.SetPolicyType("")
|
||||
switch setMetadata.GetSetKind() {
|
||||
case ipsets.ListSet:
|
||||
pType = hcn.SetPolicyTypeNestedIpSet
|
||||
case ipsets.HashSet:
|
||||
pType = hcn.SetPolicyTypeIpSet
|
||||
case ipsets.UnknownKind:
|
||||
pType = hcn.SetPolicyType("")
|
||||
}
|
||||
|
||||
// sort for easier comparison
|
||||
sort.Strings(members)
|
||||
|
||||
return &hcn.SetPolicySetting{
|
||||
Id: setMetadata.GetHashedName(),
|
||||
Name: setMetadata.GetPrefixName(),
|
||||
PolicyType: pType,
|
||||
Values: strings.Join(members, ","),
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyHNSCache asserts that HNS has the correct state.
|
||||
func VerifyHNSCache(t *testing.T, hns *hnswrapper.Hnsv2wrapperFake, expectedSetPolicies []*hcn.SetPolicySetting, expectedEndpointACLs map[string][]*hnswrapper.FakeEndpointPolicy) {
|
||||
t.Helper()
|
||||
|
||||
PrintGetAllOutput(hns)
|
||||
|
||||
// we want to evaluate both verify functions even if one fails, so don't write as verifySetPolicies() && verifyACLs() in case of short-circuiting
|
||||
success := VerifySetPolicies(t, hns, expectedSetPolicies)
|
||||
success = VerifyACLs(t, hns, expectedEndpointACLs) && success
|
||||
|
||||
if !success {
|
||||
require.FailNow(t, fmt.Sprintf("hns cache had unexpected state. printing hns cache...\n%s", hns.Cache.PrettyString()))
|
||||
}
|
||||
}
|
||||
|
||||
// VerifySetPolicies is true if HNS strictly has the expected SetPolicies.
|
||||
func VerifySetPolicies(t *testing.T, hns *hnswrapper.Hnsv2wrapperFake, expectedSetPolicies []*hcn.SetPolicySetting) bool {
|
||||
t.Helper()
|
||||
|
||||
cachedSetPolicies := hns.Cache.AllSetPolicies(common.FakeHNSNetworkID)
|
||||
|
||||
success := assert.Equal(t, len(expectedSetPolicies), len(cachedSetPolicies), "unexpected number of SetPolicies")
|
||||
for _, expectedSetPolicy := range expectedSetPolicies {
|
||||
cachedSetPolicy, ok := cachedSetPolicies[expectedSetPolicy.Id]
|
||||
success = assert.True(t, ok, fmt.Sprintf("expected SetPolicy not found. ID %s, name: %s", expectedSetPolicy.Id, expectedSetPolicy.Name)) && success
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
members := strings.Split(cachedSetPolicy.Values, ",")
|
||||
sort.Strings(members)
|
||||
copyOfCachedSetPolicy := *cachedSetPolicy
|
||||
copyOfCachedSetPolicy.Values = strings.Join(members, ",")
|
||||
|
||||
// required that the expectedSetPolicy already has sorted members
|
||||
success = assert.Equal(t, expectedSetPolicy, ©OfCachedSetPolicy, fmt.Sprintf("SetPolicy has unexpected contents. ID %s, name: %s", expectedSetPolicy.Id, expectedSetPolicy.Name)) && success
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
// verifyACLs is true if HNS strictly has the expected Endpoints and ACLs.
|
||||
func VerifyACLs(t *testing.T, hns *hnswrapper.Hnsv2wrapperFake, expectedEndpointACLs map[string][]*hnswrapper.FakeEndpointPolicy) bool {
|
||||
t.Helper()
|
||||
|
||||
cachedEndpointACLs := hns.Cache.GetAllACLs()
|
||||
|
||||
success := assert.Equal(t, len(expectedEndpointACLs), len(cachedEndpointACLs), "unexpected number of Endpoints")
|
||||
for epID, expectedACLs := range expectedEndpointACLs {
|
||||
cachedACLs, ok := cachedEndpointACLs[epID]
|
||||
success = assert.True(t, ok, fmt.Sprintf("expected ACL not found for endpoint %s", epID)) && success
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
success = assert.Equal(t, len(expectedACLs), len(cachedACLs), fmt.Sprintf("unexpected number of ACLs for Endpoint with ID: %s", epID)) && success
|
||||
for _, expectedACL := range expectedACLs {
|
||||
foundACL := false
|
||||
for _, cacheACL := range cachedACLs {
|
||||
if expectedACL.ID == cacheACL.ID {
|
||||
if cmp.Equal(expectedACL, cacheACL) {
|
||||
foundACL = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
success = assert.True(t, foundACL, fmt.Sprintf("missing expected ACL. ID: %s, full ACL: %+v", expectedACL.ID, expectedACL)) && success
|
||||
}
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
// helpful for debugging if there's a discrepancy between GetAll functions and the HNS PrettyString
|
||||
func PrintGetAllOutput(hns *hnswrapper.Hnsv2wrapperFake) {
|
||||
klog.Info("SETPOLICIES...")
|
||||
for _, setPol := range hns.Cache.AllSetPolicies(common.FakeHNSNetworkID) {
|
||||
klog.Infof("%+v", setPol)
|
||||
}
|
||||
klog.Info("Endpoint ACLs...")
|
||||
for id, acls := range hns.Cache.GetAllACLs() {
|
||||
a := make([]string, len(acls))
|
||||
for k, v := range acls {
|
||||
a[k] = fmt.Sprintf("%+v", v)
|
||||
}
|
||||
klog.Infof("%s: %s", id, strings.Join(a, ","))
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package dataplane
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/policies"
|
||||
"github.com/Azure/azure-container-networking/npm/util"
|
||||
|
@ -51,6 +53,10 @@ func NewPodMetadata(podKey, podIP, nodeName string) *PodMetadata {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *PodMetadata) Namespace() string {
|
||||
return strings.Split(p.PodKey, "/")[0]
|
||||
}
|
||||
|
||||
func newUpdateNPMPod(podMetadata *PodMetadata) *updateNPMPod {
|
||||
return &updateNPMPod{
|
||||
PodMetadata: podMetadata,
|
||||
|
|
|
@ -0,0 +1,446 @@
|
|||
package dataplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-container-networking/network/hnswrapper"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/controlplane/translation"
|
||||
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
|
||||
dptestutils "github.com/Azure/azure-container-networking/npm/pkg/dataplane/testutils"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
"github.com/pkg/errors"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
)
|
||||
|
||||
type Tag string
|
||||
|
||||
type SerialTestCase struct {
|
||||
Description string
|
||||
Actions []*Action
|
||||
*TestCaseMetadata
|
||||
}
|
||||
|
||||
type MultiJobTestCase struct {
|
||||
Description string
|
||||
Jobs map[string][]*Action
|
||||
*TestCaseMetadata
|
||||
}
|
||||
|
||||
type TestCaseMetadata struct {
|
||||
Tags []Tag
|
||||
InitialEndpoints []*hcn.HostComputeEndpoint
|
||||
DpCfg *Config
|
||||
ExpectedSetPolicies []*hcn.SetPolicySetting
|
||||
ExpectedEnpdointACLs map[string][]*hnswrapper.FakeEndpointPolicy
|
||||
}
|
||||
|
||||
// Action represents a single action (either an HNSAction or a DPAction).
|
||||
// Exactly one of HNSAction or DPAction should be non-nil.
|
||||
type Action struct {
|
||||
HNSAction
|
||||
DPAction
|
||||
}
|
||||
|
||||
type HNSAction interface {
|
||||
// Do models events in HNS
|
||||
Do(hns *hnswrapper.Hnsv2wrapperFake) error
|
||||
}
|
||||
|
||||
type EndpointCreateAction struct {
|
||||
ID string
|
||||
IP string
|
||||
}
|
||||
|
||||
func CreateEndpoint(id, ip string) *Action {
|
||||
return &Action{
|
||||
HNSAction: &EndpointCreateAction{
|
||||
ID: id,
|
||||
IP: ip,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models endpoint creation in HNS
|
||||
func (e *EndpointCreateAction) Do(hns *hnswrapper.Hnsv2wrapperFake) error {
|
||||
ep := dptestutils.Endpoint(e.ID, e.IP)
|
||||
_, err := hns.CreateEndpoint(ep)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[EndpointCreateAction] failed to create endpoint. ep: [%+v]", ep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type EndpointDeleteAction struct {
|
||||
ID string
|
||||
}
|
||||
|
||||
func DeleteEndpoint(id string) *Action {
|
||||
return &Action{
|
||||
HNSAction: &EndpointDeleteAction{
|
||||
ID: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models endpoint deletion in HNS
|
||||
func (e *EndpointDeleteAction) Do(hns *hnswrapper.Hnsv2wrapperFake) error {
|
||||
ep := &hcn.HostComputeEndpoint{
|
||||
Id: e.ID,
|
||||
}
|
||||
if err := hns.DeleteEndpoint(ep); err != nil {
|
||||
return errors.Wrapf(err, "[EndpointDeleteAction] failed to delete endpoint. ep: [%+v]", ep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DPAction interface {
|
||||
// Do models interactions with the DataPlane
|
||||
Do(dp *DataPlane) error
|
||||
}
|
||||
|
||||
type ApplyDPAction struct{}
|
||||
|
||||
func ApplyDP() *Action {
|
||||
return &Action{
|
||||
DPAction: &ApplyDPAction{},
|
||||
}
|
||||
}
|
||||
|
||||
// Do applies the dataplane
|
||||
func (*ApplyDPAction) Do(dp *DataPlane) error {
|
||||
if err := dp.ApplyDataPlane(); err != nil {
|
||||
return errors.Wrapf(err, "[ApplyDPAction] failed to apply")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReconcileDPAction struct{}
|
||||
|
||||
func ReconcileDP() *Action {
|
||||
return &Action{
|
||||
DPAction: &ReconcileDPAction{},
|
||||
}
|
||||
}
|
||||
|
||||
// Do reconciles the IPSetManager and PolicyManager
|
||||
func (*ReconcileDPAction) Do(dp *DataPlane) error {
|
||||
dp.ipsetMgr.Reconcile()
|
||||
// currently does nothing in windows
|
||||
dp.policyMgr.Reconcile()
|
||||
return nil
|
||||
}
|
||||
|
||||
type PodCreateAction struct {
|
||||
Pod *PodMetadata
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
func CreatePod(namespace, name, ip, node string, labels map[string]string) *Action {
|
||||
podKey := fmt.Sprintf("%s/%s", namespace, name)
|
||||
return &Action{
|
||||
DPAction: &PodCreateAction{
|
||||
Pod: NewPodMetadata(podKey, ip, node),
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models pod creation in the PodController
|
||||
func (p *PodCreateAction) Do(dp *DataPlane) error {
|
||||
context := fmt.Sprintf("create context: [pod: %+v. labels: %+v]", p.Pod, p.Labels)
|
||||
|
||||
nsIPSet := []*ipsets.IPSetMetadata{ipsets.NewIPSetMetadata(p.Pod.Namespace(), ipsets.Namespace)}
|
||||
// PodController technically wouldn't call this if the namespace already existed
|
||||
if err := dp.AddToLists([]*ipsets.IPSetMetadata{allNamespaces}, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[PodCreateAction] failed to add ns set to all-namespaces list. %s", context)
|
||||
}
|
||||
|
||||
if err := dp.AddToSets(nsIPSet, p.Pod); err != nil {
|
||||
return errors.Wrapf(err, "[PodCreateAction] failed to add pod ip to ns set. %s", context)
|
||||
}
|
||||
|
||||
for key, val := range p.Labels {
|
||||
keyVal := fmt.Sprintf("%s:%s", key, val)
|
||||
labelIPSets := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(key, ipsets.KeyLabelOfPod),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfPod),
|
||||
}
|
||||
|
||||
if err := dp.AddToSets(labelIPSets, p.Pod); err != nil {
|
||||
return errors.Wrapf(err, "[PodCreateAction] failed to add pod ip to label sets %+v. %s", labelIPSets, context)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PodUpdateAction struct {
|
||||
OldPod *PodMetadata
|
||||
NewPod *PodMetadata
|
||||
LabelsToRemove map[string]string
|
||||
LabelsToAdd map[string]string
|
||||
}
|
||||
|
||||
func UpdatePod(namespace, name, oldIP, oldNode, newIP, newNode string, labelsToRemove, labelsToAdd map[string]string) *Action {
|
||||
podKey := fmt.Sprintf("%s/%s", namespace, name)
|
||||
return &Action{
|
||||
DPAction: &PodUpdateAction{
|
||||
OldPod: NewPodMetadata(podKey, oldIP, oldNode),
|
||||
NewPod: NewPodMetadata(podKey, newIP, newNode),
|
||||
LabelsToRemove: labelsToRemove,
|
||||
LabelsToAdd: labelsToAdd,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UpdatePodLabels(namespace, name, ip, node string, labelsToRemove, labelsToAdd map[string]string) *Action {
|
||||
return UpdatePod(namespace, name, ip, node, ip, node, labelsToRemove, labelsToAdd)
|
||||
}
|
||||
|
||||
// Do models pod updates in the PodController
|
||||
func (p *PodUpdateAction) Do(dp *DataPlane) error {
|
||||
context := fmt.Sprintf("update context: [old pod: %+v. current IP: %+v. old labels: %+v. new labels: %+v]", p.OldPod, p.NewPod.PodIP, p.LabelsToRemove, p.LabelsToAdd)
|
||||
|
||||
// think it's impossible for this to be called on an UPDATE
|
||||
// dp.AddToLists([]*ipsets.IPSetMetadata{allNamespaces}, []*ipsets.IPSetMetadata{nsIPSet})
|
||||
|
||||
for k, v := range p.LabelsToRemove {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
sets := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfPod),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfPod),
|
||||
}
|
||||
for _, toRemoveSet := range sets {
|
||||
if err := dp.RemoveFromSets([]*ipsets.IPSetMetadata{toRemoveSet}, p.OldPod); err != nil {
|
||||
return errors.Wrapf(err, "[PodUpdateAction] failed to remove old pod ip from set %s. %s", toRemoveSet.GetPrefixName(), context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range p.LabelsToAdd {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
sets := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfPod),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfPod),
|
||||
}
|
||||
for _, toAddSet := range sets {
|
||||
if err := dp.AddToSets([]*ipsets.IPSetMetadata{toAddSet}, p.NewPod); err != nil {
|
||||
return errors.Wrapf(err, "[PodUpdateAction] failed to add new pod ip to set %s. %s", toAddSet.GetPrefixName(), context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PodDeleteAction struct {
|
||||
Pod *PodMetadata
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
func DeletePod(namespace, name, ip string, labels map[string]string) *Action {
|
||||
podKey := fmt.Sprintf("%s/%s", namespace, name)
|
||||
return &Action{
|
||||
DPAction: &PodDeleteAction{
|
||||
// currently, the PodController doesn't share the node name
|
||||
Pod: NewPodMetadata(podKey, ip, ""),
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models pod deletion in the PodController
|
||||
func (p *PodDeleteAction) Do(dp *DataPlane) error {
|
||||
context := fmt.Sprintf("delete context: [pod: %+v. labels: %+v]", p.Pod, p.Labels)
|
||||
|
||||
nsIPSet := []*ipsets.IPSetMetadata{ipsets.NewIPSetMetadata(p.Pod.Namespace(), ipsets.Namespace)}
|
||||
if err := dp.RemoveFromSets(nsIPSet, p.Pod); err != nil {
|
||||
return errors.Wrapf(err, "[PodDeleteAction] failed to remove pod ip from ns set. %s", context)
|
||||
}
|
||||
|
||||
for key, val := range p.Labels {
|
||||
keyVal := fmt.Sprintf("%s:%s", key, val)
|
||||
labelIPSets := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(key, ipsets.KeyLabelOfPod),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfPod),
|
||||
}
|
||||
|
||||
if err := dp.RemoveFromSets(labelIPSets, p.Pod); err != nil {
|
||||
return errors.Wrapf(err, "[PodDeleteAction] failed to remove pod ip from label set %+v. %s", labelIPSets, context)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type NamespaceCreateAction struct {
|
||||
NS string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
func CreateNamespace(ns string, labels map[string]string) *Action {
|
||||
return &Action{
|
||||
DPAction: &NamespaceCreateAction{
|
||||
NS: ns,
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models namespace creation in the NamespaceController
|
||||
func (n *NamespaceCreateAction) Do(dp *DataPlane) error {
|
||||
nsIPSet := []*ipsets.IPSetMetadata{ipsets.NewIPSetMetadata(n.NS, ipsets.Namespace)}
|
||||
|
||||
listsToAddTo := []*ipsets.IPSetMetadata{allNamespaces}
|
||||
for k, v := range n.Labels {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
listsToAddTo = append(listsToAddTo,
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfNamespace),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfNamespace))
|
||||
}
|
||||
|
||||
if err := dp.AddToLists(listsToAddTo, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[NamespaceCreateAction] failed to add ns ipset to all lists. Action: %+v", n)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type NamespaceUpdateAction struct {
|
||||
NS string
|
||||
LabelsToRemove map[string]string
|
||||
LabelsToAdd map[string]string
|
||||
}
|
||||
|
||||
func UpdateNamespace(ns string, labelsToRemove, labelsToAdd map[string]string) *Action {
|
||||
return &Action{
|
||||
DPAction: &NamespaceUpdateAction{
|
||||
NS: ns,
|
||||
LabelsToRemove: labelsToRemove,
|
||||
LabelsToAdd: labelsToAdd,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models namespace updates in the NamespaceController
|
||||
func (n *NamespaceUpdateAction) Do(dp *DataPlane) error {
|
||||
nsIPSet := []*ipsets.IPSetMetadata{ipsets.NewIPSetMetadata(n.NS, ipsets.Namespace)}
|
||||
|
||||
for k, v := range n.LabelsToRemove {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
lists := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfNamespace),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfNamespace),
|
||||
}
|
||||
for _, listToRemoveFrom := range lists {
|
||||
if err := dp.RemoveFromList(listToRemoveFrom, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[NamespaceUpdateAction] failed to remove ns ipset from list %s. Action: %+v", listToRemoveFrom.GetPrefixName(), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range n.LabelsToAdd {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
lists := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfNamespace),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfNamespace),
|
||||
}
|
||||
for _, listToAddTo := range lists {
|
||||
if err := dp.RemoveFromList(listToAddTo, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[NamespaceUpdateAction] failed to add ns ipset to list %s. Action: %+v", listToAddTo.GetPrefixName(), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type NamespaceDeleteAction struct {
|
||||
NS string
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
func DeleteNamespace(ns string, labels map[string]string) *Action {
|
||||
return &Action{
|
||||
DPAction: &NamespaceDeleteAction{
|
||||
NS: ns,
|
||||
Labels: labels,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models namespace deletion in the NamespaceController
|
||||
func (n *NamespaceDeleteAction) Do(dp *DataPlane) error {
|
||||
nsIPSet := []*ipsets.IPSetMetadata{ipsets.NewIPSetMetadata(n.NS, ipsets.Namespace)}
|
||||
|
||||
for k, v := range n.Labels {
|
||||
keyVal := fmt.Sprintf("%s:%s", k, v)
|
||||
lists := []*ipsets.IPSetMetadata{
|
||||
ipsets.NewIPSetMetadata(k, ipsets.KeyLabelOfNamespace),
|
||||
ipsets.NewIPSetMetadata(keyVal, ipsets.KeyValueLabelOfNamespace),
|
||||
}
|
||||
for _, listToRemoveFrom := range lists {
|
||||
if err := dp.RemoveFromList(listToRemoveFrom, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[NamespaceDeleteAction] failed to remove ns ipset from list %s. Action: %+v", listToRemoveFrom.GetPrefixName(), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := dp.RemoveFromList(allNamespaces, nsIPSet); err != nil {
|
||||
return errors.Wrapf(err, "[NamespaceDeleteAction] failed to remove ns ipset from all-namespaces list. Action: %+v", n)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PolicyUpdateAction struct {
|
||||
Policy *networkingv1.NetworkPolicy
|
||||
}
|
||||
|
||||
func UpdatePolicy(policy *networkingv1.NetworkPolicy) *Action {
|
||||
return &Action{
|
||||
DPAction: &PolicyUpdateAction{
|
||||
Policy: policy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Do models policy updates in the NetworkPolicyController
|
||||
func (p *PolicyUpdateAction) Do(dp *DataPlane) error {
|
||||
npmNetPol, err := translation.TranslatePolicy(p.Policy)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[PolicyUpdateAction] failed to translate policy with key %s/%s", p.Policy.Namespace, p.Policy.Name)
|
||||
}
|
||||
|
||||
if err := dp.UpdatePolicy(npmNetPol); err != nil {
|
||||
return errors.Wrapf(err, "[PolicyUpdateAction] failed to update policy with key %s/%s", p.Policy.Namespace, p.Policy.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PolicyDeleteAction struct {
|
||||
Namespace string
|
||||
Name string
|
||||
}
|
||||
|
||||
func DeletePolicy(namespace, name string) *Action {
|
||||
return &Action{
|
||||
DPAction: &PolicyDeleteAction{
|
||||
Namespace: namespace,
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func DeletePolicyByObject(policy *networkingv1.NetworkPolicy) *Action {
|
||||
return DeletePolicy(policy.Namespace, policy.Name)
|
||||
}
|
||||
|
||||
// Do models policy deletion in the NetworkPolicyController
|
||||
func (p *PolicyDeleteAction) Do(dp *DataPlane) error {
|
||||
policyKey := fmt.Sprintf("%s/%s", p.Namespace, p.Name)
|
||||
if err := dp.RemovePolicy(policyKey); err != nil {
|
||||
return errors.Wrapf(err, "[PolicyDeleteAction] failed to update policy with key %s", policyKey)
|
||||
}
|
||||
return nil
|
||||
}
|
Загрузка…
Ссылка в новой задаче