diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml index ec164bba9c3..538bf3b6f43 100644 --- a/build/yamls/antrea-aks.yml +++ b/build/yamls/antrea-aks.yml @@ -1891,6 +1891,14 @@ rules: - get - watch - list +- apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -1952,6 +1960,14 @@ rules: - patch - create - delete +- apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2143,6 +2159,14 @@ rules: - clustergroups/status verbs: - update +- apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list - apiGroups: - clusterinformation.antrea.tanzu.vmware.com resources: @@ -2303,6 +2327,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int @@ -2451,6 +2478,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. @@ -2495,7 +2525,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 namespace: kube-system --- apiVersion: v1 @@ -2615,7 +2645,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 name: antrea-config - name: antrea-controller-tls secret: @@ -2924,7 +2954,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml index e05beae5bc9..f9b2a875b98 100644 --- a/build/yamls/antrea-eks.yml +++ b/build/yamls/antrea-eks.yml @@ -1891,6 +1891,14 @@ rules: - get - watch - list +- apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -1952,6 +1960,14 @@ rules: - patch - create - delete +- apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2143,6 +2159,14 @@ rules: - clustergroups/status verbs: - update +- apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list - apiGroups: - clusterinformation.antrea.tanzu.vmware.com resources: @@ -2303,6 +2327,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int @@ -2451,6 +2478,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. @@ -2495,7 +2525,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 namespace: kube-system --- apiVersion: v1 @@ -2615,7 +2645,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 name: antrea-config - name: antrea-controller-tls secret: @@ -2926,7 +2956,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-t45hm85989 + name: antrea-config-bm46tm9f88 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml index a6e66f1a84a..5f39eecf360 100644 --- a/build/yamls/antrea-gke.yml +++ b/build/yamls/antrea-gke.yml @@ -1891,6 +1891,14 @@ rules: - get - watch - list +- apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -1952,6 +1960,14 @@ rules: - patch - create - delete +- apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2143,6 +2159,14 @@ rules: - clustergroups/status verbs: - update +- apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list - apiGroups: - clusterinformation.antrea.tanzu.vmware.com resources: @@ -2303,6 +2327,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int @@ -2451,6 +2478,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. @@ -2495,7 +2525,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-c2c88t7544 + name: antrea-config-5c954cd56b namespace: kube-system --- apiVersion: v1 @@ -2615,7 +2645,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-c2c88t7544 + name: antrea-config-5c954cd56b name: antrea-config - name: antrea-controller-tls secret: @@ -2927,7 +2957,7 @@ spec: path: /home/kubernetes/bin name: host-cni-bin - configMap: - name: antrea-config-c2c88t7544 + name: antrea-config-5c954cd56b name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml index bcec31cb86b..66c9bdb8b3a 100644 --- a/build/yamls/antrea-ipsec.yml +++ b/build/yamls/antrea-ipsec.yml @@ -1891,6 +1891,14 @@ rules: - get - watch - list +- apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -1952,6 +1960,14 @@ rules: - patch - create - delete +- apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2143,6 +2159,14 @@ rules: - clustergroups/status verbs: - update +- apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list - apiGroups: - clusterinformation.antrea.tanzu.vmware.com resources: @@ -2303,6 +2327,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int @@ -2456,6 +2483,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. @@ -2500,7 +2530,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-f9cht7228c + name: antrea-config-29788ckmb7 namespace: kube-system --- apiVersion: v1 @@ -2629,7 +2659,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-f9cht7228c + name: antrea-config-29788ckmb7 name: antrea-config - name: antrea-controller-tls secret: @@ -2973,7 +3003,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-f9cht7228c + name: antrea-config-29788ckmb7 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml index 061fa6c5ad2..a34ed4ce857 100644 --- a/build/yamls/antrea.yml +++ b/build/yamls/antrea.yml @@ -1891,6 +1891,14 @@ rules: - get - watch - list +- apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -1952,6 +1960,14 @@ rules: - patch - create - delete +- apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -2143,6 +2159,14 @@ rules: - clustergroups/status verbs: - update +- apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list - apiGroups: - clusterinformation.antrea.tanzu.vmware.com resources: @@ -2303,6 +2327,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int @@ -2456,6 +2483,9 @@ data: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false + # Enable controlling SNAT IPs of Pod egress traffic. + # Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. @@ -2500,7 +2530,7 @@ metadata: annotations: {} labels: app: antrea - name: antrea-config-dhd2kgc9db + name: antrea-config-f27tdcgm22 namespace: kube-system --- apiVersion: v1 @@ -2620,7 +2650,7 @@ spec: key: node-role.kubernetes.io/master volumes: - configMap: - name: antrea-config-dhd2kgc9db + name: antrea-config-f27tdcgm22 name: antrea-config - name: antrea-controller-tls secret: @@ -2929,7 +2959,7 @@ spec: operator: Exists volumes: - configMap: - name: antrea-config-dhd2kgc9db + name: antrea-config-f27tdcgm22 name: antrea-config - hostPath: path: /etc/cni/net.d diff --git a/build/yamls/base/agent-rbac.yml b/build/yamls/base/agent-rbac.yml index 26bc173aa06..1a96d7d12ec 100644 --- a/build/yamls/base/agent-rbac.yml +++ b/build/yamls/base/agent-rbac.yml @@ -66,6 +66,14 @@ rules: - get - watch - list + - apiGroups: + - controlplane.antrea.io + resources: + - egressgroups + verbs: + - get + - watch + - list - apiGroups: - controlplane.antrea.tanzu.vmware.com - controlplane.antrea.io @@ -133,6 +141,14 @@ rules: - patch - create - delete + - apiGroups: + - egress.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/build/yamls/base/conf/antrea-agent.conf b/build/yamls/base/conf/antrea-agent.conf index 52fe04312dd..b9e8641c25f 100644 --- a/build/yamls/base/conf/antrea-agent.conf +++ b/build/yamls/base/conf/antrea-agent.conf @@ -28,6 +28,9 @@ featureGates: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false +# Enable controlling SNAT IPs of Pod egress traffic. +# Egress: false + # Name of the OpenVSwitch bridge antrea-agent will create and use. # Make sure it doesn't conflict with your existing OpenVSwitch bridges. #ovsBridge: br-int diff --git a/build/yamls/base/conf/antrea-controller.conf b/build/yamls/base/conf/antrea-controller.conf index d555965e78f..dc34c1747dc 100644 --- a/build/yamls/base/conf/antrea-controller.conf +++ b/build/yamls/base/conf/antrea-controller.conf @@ -11,6 +11,9 @@ featureGates: # Enable collecting and exposing NetworkPolicy statistics. # NetworkPolicyStats: false +# Enable controlling SNAT IPs of Pod egress traffic. +# Egress: false + # The port for the antrea-controller APIServer to serve on. # Note that if it's set to another value, the `containerPort` of the `api` port of the # `antrea-controller` container must be set to the same value. diff --git a/build/yamls/base/controller-rbac.yml b/build/yamls/base/controller-rbac.yml index 75774d08955..91c9e7b3304 100644 --- a/build/yamls/base/controller-rbac.yml +++ b/build/yamls/base/controller-rbac.yml @@ -183,6 +183,14 @@ rules: - clustergroups/status verbs: - update + - apiGroups: + - crd.antrea.io + resources: + - egresses + verbs: + - get + - watch + - list # Deprecated in v1.0.0. - apiGroups: - clusterinformation.antrea.tanzu.vmware.com diff --git a/cmd/antrea-controller/controller.go b/cmd/antrea-controller/controller.go index 4988f11894f..f84e2f82e31 100644 --- a/cmd/antrea-controller/controller.go +++ b/cmd/antrea-controller/controller.go @@ -38,6 +38,8 @@ import ( "github.com/vmware-tanzu/antrea/pkg/clusteridentity" "github.com/vmware-tanzu/antrea/pkg/controller/crdmirroring" "github.com/vmware-tanzu/antrea/pkg/controller/crdmirroring/crdhandler" + "github.com/vmware-tanzu/antrea/pkg/controller/egress" + egressstore "github.com/vmware-tanzu/antrea/pkg/controller/egress/store" "github.com/vmware-tanzu/antrea/pkg/controller/grouping" "github.com/vmware-tanzu/antrea/pkg/controller/metrics" "github.com/vmware-tanzu/antrea/pkg/controller/networkpolicy" @@ -111,6 +113,7 @@ func run(o *Options) error { tierInformer := crdInformerFactory.Crd().V1alpha1().Tiers() tfInformer := crdInformerFactory.Crd().V1alpha1().Traceflows() cgInformer := crdInformerFactory.Crd().V1alpha2().ClusterGroups() + egressInformer := crdInformerFactory.Crd().V1alpha2().Egresses() clusterIdentityAllocator := clusteridentity.NewClusterIdentityAllocator( env.GetAntreaNamespace(), @@ -122,6 +125,7 @@ func run(o *Options) error { addressGroupStore := store.NewAddressGroupStore() appliedToGroupStore := store.NewAppliedToGroupStore() networkPolicyStore := store.NewNetworkPolicyStore() + egressGroupStore := egressstore.NewEgressGroupStore() groupStore := store.NewGroupStore() groupEntityIndex := grouping.NewGroupEntityIndex() groupEntityController := grouping.NewGroupEntityController(groupEntityIndex, podInformer, namespaceInformer, eeInformer) @@ -216,6 +220,11 @@ func run(o *Options) error { controllerMonitor := monitor.NewControllerMonitor(crdClient, legacyCRDClient, nodeInformer, controllerQuerier) + var egressController *egress.EgressController + if features.DefaultFeatureGate.Enabled(features.Egress) { + egressController = egress.NewEgressController(groupEntityIndex, egressInformer, egressGroupStore) + } + var traceflowController *traceflow.Controller if features.DefaultFeatureGate.Enabled(features.Traceflow) { traceflowController = traceflow.NewTraceflowController(crdClient, podInformer, tfInformer) @@ -254,6 +263,7 @@ func run(o *Options) error { appliedToGroupStore, networkPolicyStore, groupStore, + egressGroupStore, controllerQuerier, endpointQuerier, networkPolicyController, @@ -324,6 +334,9 @@ func run(o *Options) error { go eeMirroringController.Run(stopCh) } } + if features.DefaultFeatureGate.Enabled(features.Egress) { + go egressController.Run(stopCh) + } <-stopCh klog.Info("Stopping Antrea controller") @@ -339,6 +352,7 @@ func createAPIServerConfig(kubeconfig string, appliedToGroupStore storage.Interface, networkPolicyStore storage.Interface, groupStore storage.Interface, + egressGroupStore storage.Interface, controllerQuerier querier.ControllerQuerier, endpointQuerier networkpolicy.EndpointQuerier, npController *networkpolicy.NetworkPolicyController, @@ -396,6 +410,7 @@ func createAPIServerConfig(kubeconfig string, appliedToGroupStore, networkPolicyStore, groupStore, + egressGroupStore, caCertController, statsAggregator, controllerQuerier, diff --git a/pkg/apis/controlplane/register.go b/pkg/apis/controlplane/register.go index bc543e435ab..6de7af6f256 100644 --- a/pkg/apis/controlplane/register.go +++ b/pkg/apis/controlplane/register.go @@ -57,6 +57,9 @@ func addKnownTypes(scheme *runtime.Scheme) error { &NodeStatsSummary{}, &ClusterGroupMembers{}, &GroupAssociation{}, + &EgressGroup{}, + &EgressGroupPatch{}, + &EgressGroupList{}, ) return nil } diff --git a/pkg/apis/controlplane/types.go b/pkg/apis/controlplane/types.go index a9fd4b93545..e8345b180fa 100644 --- a/pkg/apis/controlplane/types.go +++ b/pkg/apis/controlplane/types.go @@ -330,3 +330,28 @@ type GroupAssociation struct { // Pod/ExternalEntity being queried. AssociatedGroups []GroupReference } + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type EgressGroup struct { + metav1.TypeMeta + metav1.ObjectMeta + // GroupMembers is a list of GroupMember selected by this group. + GroupMembers []GroupMember +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupPatch describes the incremental update of an EgressGroup. +type EgressGroupPatch struct { + metav1.TypeMeta + metav1.ObjectMeta + AddedGroupMembers []GroupMember + RemovedGroupMembers []GroupMember +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupList is a list of EgressGroup objects. +type EgressGroupList struct { + metav1.TypeMeta + metav1.ListMeta + Items []EgressGroup +} diff --git a/pkg/apis/controlplane/v1beta1/conversion.go b/pkg/apis/controlplane/v1beta1/conversion.go index a0d7f9fe951..129218b2f3b 100644 --- a/pkg/apis/controlplane/v1beta1/conversion.go +++ b/pkg/apis/controlplane/v1beta1/conversion.go @@ -31,7 +31,7 @@ func init() { // addConversionFuncs adds non-generated conversion functions to the given scheme. func addConversionFuncs(scheme *runtime.Scheme) error { - for _, kind := range []string{"AppliedToGroup", "AddressGroup", "NetworkPolicy"} { + for _, kind := range []string{"AppliedToGroup", "AddressGroup", "NetworkPolicy", "EgressGroup"} { err := scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind(kind), func(label, value string) (string, string, error) { switch label { diff --git a/pkg/apis/controlplane/v1beta2/generated.pb.go b/pkg/apis/controlplane/v1beta2/generated.pb.go index 8e3ebd8e5d5..3c5b6057c80 100644 --- a/pkg/apis/controlplane/v1beta2/generated.pb.go +++ b/pkg/apis/controlplane/v1beta2/generated.pb.go @@ -242,10 +242,94 @@ func (m *ClusterGroupMembers) XXX_DiscardUnknown() { var xxx_messageInfo_ClusterGroupMembers proto.InternalMessageInfo +func (m *EgressGroup) Reset() { *m = EgressGroup{} } +func (*EgressGroup) ProtoMessage() {} +func (*EgressGroup) Descriptor() ([]byte, []int) { + return fileDescriptor_d31898dc88dbbf6e, []int{7} +} +func (m *EgressGroup) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EgressGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *EgressGroup) XXX_Merge(src proto.Message) { + xxx_messageInfo_EgressGroup.Merge(m, src) +} +func (m *EgressGroup) XXX_Size() int { + return m.Size() +} +func (m *EgressGroup) XXX_DiscardUnknown() { + xxx_messageInfo_EgressGroup.DiscardUnknown(m) +} + +var xxx_messageInfo_EgressGroup proto.InternalMessageInfo + +func (m *EgressGroupList) Reset() { *m = EgressGroupList{} } +func (*EgressGroupList) ProtoMessage() {} +func (*EgressGroupList) Descriptor() ([]byte, []int) { + return fileDescriptor_d31898dc88dbbf6e, []int{8} +} +func (m *EgressGroupList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EgressGroupList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *EgressGroupList) XXX_Merge(src proto.Message) { + xxx_messageInfo_EgressGroupList.Merge(m, src) +} +func (m *EgressGroupList) XXX_Size() int { + return m.Size() +} +func (m *EgressGroupList) XXX_DiscardUnknown() { + xxx_messageInfo_EgressGroupList.DiscardUnknown(m) +} + +var xxx_messageInfo_EgressGroupList proto.InternalMessageInfo + +func (m *EgressGroupPatch) Reset() { *m = EgressGroupPatch{} } +func (*EgressGroupPatch) ProtoMessage() {} +func (*EgressGroupPatch) Descriptor() ([]byte, []int) { + return fileDescriptor_d31898dc88dbbf6e, []int{9} +} +func (m *EgressGroupPatch) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EgressGroupPatch) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *EgressGroupPatch) XXX_Merge(src proto.Message) { + xxx_messageInfo_EgressGroupPatch.Merge(m, src) +} +func (m *EgressGroupPatch) XXX_Size() int { + return m.Size() +} +func (m *EgressGroupPatch) XXX_DiscardUnknown() { + xxx_messageInfo_EgressGroupPatch.DiscardUnknown(m) +} + +var xxx_messageInfo_EgressGroupPatch proto.InternalMessageInfo + func (m *ExternalEntityReference) Reset() { *m = ExternalEntityReference{} } func (*ExternalEntityReference) ProtoMessage() {} func (*ExternalEntityReference) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{7} + return fileDescriptor_d31898dc88dbbf6e, []int{10} } func (m *ExternalEntityReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -273,7 +357,7 @@ var xxx_messageInfo_ExternalEntityReference proto.InternalMessageInfo func (m *GroupAssociation) Reset() { *m = GroupAssociation{} } func (*GroupAssociation) ProtoMessage() {} func (*GroupAssociation) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{8} + return fileDescriptor_d31898dc88dbbf6e, []int{11} } func (m *GroupAssociation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -301,7 +385,7 @@ var xxx_messageInfo_GroupAssociation proto.InternalMessageInfo func (m *GroupMember) Reset() { *m = GroupMember{} } func (*GroupMember) ProtoMessage() {} func (*GroupMember) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{9} + return fileDescriptor_d31898dc88dbbf6e, []int{12} } func (m *GroupMember) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -329,7 +413,7 @@ var xxx_messageInfo_GroupMember proto.InternalMessageInfo func (m *GroupReference) Reset() { *m = GroupReference{} } func (*GroupReference) ProtoMessage() {} func (*GroupReference) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{10} + return fileDescriptor_d31898dc88dbbf6e, []int{13} } func (m *GroupReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -357,7 +441,7 @@ var xxx_messageInfo_GroupReference proto.InternalMessageInfo func (m *IPBlock) Reset() { *m = IPBlock{} } func (*IPBlock) ProtoMessage() {} func (*IPBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{11} + return fileDescriptor_d31898dc88dbbf6e, []int{14} } func (m *IPBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -385,7 +469,7 @@ var xxx_messageInfo_IPBlock proto.InternalMessageInfo func (m *IPNet) Reset() { *m = IPNet{} } func (*IPNet) ProtoMessage() {} func (*IPNet) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{12} + return fileDescriptor_d31898dc88dbbf6e, []int{15} } func (m *IPNet) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -413,7 +497,7 @@ var xxx_messageInfo_IPNet proto.InternalMessageInfo func (m *NamedPort) Reset() { *m = NamedPort{} } func (*NamedPort) ProtoMessage() {} func (*NamedPort) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{13} + return fileDescriptor_d31898dc88dbbf6e, []int{16} } func (m *NamedPort) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -441,7 +525,7 @@ var xxx_messageInfo_NamedPort proto.InternalMessageInfo func (m *NetworkPolicy) Reset() { *m = NetworkPolicy{} } func (*NetworkPolicy) ProtoMessage() {} func (*NetworkPolicy) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{14} + return fileDescriptor_d31898dc88dbbf6e, []int{17} } func (m *NetworkPolicy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -469,7 +553,7 @@ var xxx_messageInfo_NetworkPolicy proto.InternalMessageInfo func (m *NetworkPolicyList) Reset() { *m = NetworkPolicyList{} } func (*NetworkPolicyList) ProtoMessage() {} func (*NetworkPolicyList) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{15} + return fileDescriptor_d31898dc88dbbf6e, []int{18} } func (m *NetworkPolicyList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -497,7 +581,7 @@ var xxx_messageInfo_NetworkPolicyList proto.InternalMessageInfo func (m *NetworkPolicyNodeStatus) Reset() { *m = NetworkPolicyNodeStatus{} } func (*NetworkPolicyNodeStatus) ProtoMessage() {} func (*NetworkPolicyNodeStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{16} + return fileDescriptor_d31898dc88dbbf6e, []int{19} } func (m *NetworkPolicyNodeStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -525,7 +609,7 @@ var xxx_messageInfo_NetworkPolicyNodeStatus proto.InternalMessageInfo func (m *NetworkPolicyPeer) Reset() { *m = NetworkPolicyPeer{} } func (*NetworkPolicyPeer) ProtoMessage() {} func (*NetworkPolicyPeer) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{17} + return fileDescriptor_d31898dc88dbbf6e, []int{20} } func (m *NetworkPolicyPeer) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -553,7 +637,7 @@ var xxx_messageInfo_NetworkPolicyPeer proto.InternalMessageInfo func (m *NetworkPolicyReference) Reset() { *m = NetworkPolicyReference{} } func (*NetworkPolicyReference) ProtoMessage() {} func (*NetworkPolicyReference) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{18} + return fileDescriptor_d31898dc88dbbf6e, []int{21} } func (m *NetworkPolicyReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -581,7 +665,7 @@ var xxx_messageInfo_NetworkPolicyReference proto.InternalMessageInfo func (m *NetworkPolicyRule) Reset() { *m = NetworkPolicyRule{} } func (*NetworkPolicyRule) ProtoMessage() {} func (*NetworkPolicyRule) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{19} + return fileDescriptor_d31898dc88dbbf6e, []int{22} } func (m *NetworkPolicyRule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -609,7 +693,7 @@ var xxx_messageInfo_NetworkPolicyRule proto.InternalMessageInfo func (m *NetworkPolicyStats) Reset() { *m = NetworkPolicyStats{} } func (*NetworkPolicyStats) ProtoMessage() {} func (*NetworkPolicyStats) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{20} + return fileDescriptor_d31898dc88dbbf6e, []int{23} } func (m *NetworkPolicyStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -637,7 +721,7 @@ var xxx_messageInfo_NetworkPolicyStats proto.InternalMessageInfo func (m *NetworkPolicyStatus) Reset() { *m = NetworkPolicyStatus{} } func (*NetworkPolicyStatus) ProtoMessage() {} func (*NetworkPolicyStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{21} + return fileDescriptor_d31898dc88dbbf6e, []int{24} } func (m *NetworkPolicyStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -665,7 +749,7 @@ var xxx_messageInfo_NetworkPolicyStatus proto.InternalMessageInfo func (m *NodeStatsSummary) Reset() { *m = NodeStatsSummary{} } func (*NodeStatsSummary) ProtoMessage() {} func (*NodeStatsSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{22} + return fileDescriptor_d31898dc88dbbf6e, []int{25} } func (m *NodeStatsSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -693,7 +777,7 @@ var xxx_messageInfo_NodeStatsSummary proto.InternalMessageInfo func (m *PodReference) Reset() { *m = PodReference{} } func (*PodReference) ProtoMessage() {} func (*PodReference) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{23} + return fileDescriptor_d31898dc88dbbf6e, []int{26} } func (m *PodReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -721,7 +805,7 @@ var xxx_messageInfo_PodReference proto.InternalMessageInfo func (m *Service) Reset() { *m = Service{} } func (*Service) ProtoMessage() {} func (*Service) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{24} + return fileDescriptor_d31898dc88dbbf6e, []int{27} } func (m *Service) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -749,7 +833,7 @@ var xxx_messageInfo_Service proto.InternalMessageInfo func (m *ServiceReference) Reset() { *m = ServiceReference{} } func (*ServiceReference) ProtoMessage() {} func (*ServiceReference) Descriptor() ([]byte, []int) { - return fileDescriptor_d31898dc88dbbf6e, []int{25} + return fileDescriptor_d31898dc88dbbf6e, []int{28} } func (m *ServiceReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -782,6 +866,9 @@ func init() { proto.RegisterType((*AppliedToGroupList)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.AppliedToGroupList") proto.RegisterType((*AppliedToGroupPatch)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.AppliedToGroupPatch") proto.RegisterType((*ClusterGroupMembers)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.ClusterGroupMembers") + proto.RegisterType((*EgressGroup)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.EgressGroup") + proto.RegisterType((*EgressGroupList)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.EgressGroupList") + proto.RegisterType((*EgressGroupPatch)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.EgressGroupPatch") proto.RegisterType((*ExternalEntityReference)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.ExternalEntityReference") proto.RegisterType((*GroupAssociation)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.GroupAssociation") proto.RegisterType((*GroupMember)(nil), "github.com.vmware_tanzu.antrea.pkg.apis.controlplane.v1beta2.GroupMember") @@ -808,119 +895,123 @@ func init() { } var fileDescriptor_d31898dc88dbbf6e = []byte{ - // 1790 bytes of a gzipped FileDescriptorProto + // 1846 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x59, 0xcb, 0x6f, 0x23, 0x49, - 0x19, 0x4f, 0xfb, 0x91, 0xc4, 0x5f, 0x9c, 0x8c, 0x53, 0xd9, 0x61, 0xcc, 0x30, 0xd8, 0xd9, 0xe6, - 0xa1, 0x1c, 0x98, 0xf6, 0x4e, 0x18, 0x60, 0x24, 0x16, 0xa1, 0x78, 0x92, 0x0d, 0x86, 0x8c, 0xc7, - 0xaa, 0x64, 0x2e, 0x08, 0x09, 0x3a, 0xdd, 0x65, 0xa7, 0x89, 0xdd, 0xd5, 0x5b, 0x5d, 0xce, 0x4e, - 0x16, 0x09, 0x81, 0x38, 0x2d, 0x07, 0x9e, 0x17, 0x4e, 0xdc, 0x56, 0xfb, 0x1f, 0x20, 0xc1, 0x8d, - 0xdb, 0x1c, 0xf7, 0xb8, 0x1c, 0xb0, 0x18, 0xaf, 0xe0, 0xca, 0x01, 0x09, 0xa1, 0x9c, 0x50, 0x55, - 0x57, 0x3f, 0x9d, 0x6c, 0xb2, 0xd8, 0x89, 0x90, 0xe0, 0x14, 0x77, 0xd5, 0x57, 0xdf, 0xef, 0x57, - 0xdf, 0xab, 0xbe, 0xaa, 0xc0, 0x5e, 0xcf, 0xe1, 0x47, 0xc3, 0x43, 0xc3, 0xa2, 0x83, 0xc6, 0xc9, - 0xe0, 0x2d, 0x93, 0x91, 0xfb, 0xdc, 0x74, 0xdf, 0x1e, 0x36, 0x4c, 0x97, 0x33, 0x62, 0x36, 0xbc, - 0xe3, 0x5e, 0xc3, 0xf4, 0x1c, 0xbf, 0x61, 0x51, 0x97, 0x33, 0xda, 0xf7, 0xfa, 0xa6, 0x4b, 0x1a, - 0x27, 0x0f, 0x0e, 0x09, 0x37, 0x37, 0x1b, 0x3d, 0xe2, 0x12, 0x66, 0x72, 0x62, 0x1b, 0x1e, 0xa3, - 0x9c, 0xa2, 0xd7, 0x63, 0x6d, 0x46, 0xa0, 0xed, 0xbb, 0x52, 0x9b, 0x11, 0x68, 0x33, 0xbc, 0xe3, - 0x9e, 0x21, 0xb4, 0x19, 0x49, 0x6d, 0x86, 0xd2, 0x76, 0xf7, 0x7e, 0x82, 0x4b, 0x8f, 0xf6, 0x68, - 0x43, 0x2a, 0x3d, 0x1c, 0x76, 0xe5, 0x97, 0xfc, 0x90, 0xbf, 0x02, 0xb0, 0xbb, 0x6f, 0x5c, 0x95, - 0xba, 0xcf, 0x4d, 0xee, 0x37, 0x4e, 0x1e, 0x98, 0x7d, 0xef, 0xc8, 0x7c, 0x90, 0x25, 0x7d, 0xf7, - 0xe1, 0xf1, 0x23, 0xdf, 0x70, 0xa8, 0x90, 0x1d, 0x98, 0xd6, 0x91, 0xe3, 0x12, 0x76, 0x1a, 0x2f, - 0x1e, 0x10, 0x6e, 0x36, 0x4e, 0x26, 0x57, 0x35, 0x2e, 0x5a, 0xc5, 0x86, 0x2e, 0x77, 0x06, 0x64, - 0x62, 0xc1, 0x97, 0x2f, 0x5b, 0xe0, 0x5b, 0x47, 0x64, 0x60, 0x4e, 0xac, 0xfb, 0xe2, 0x45, 0xeb, - 0x86, 0xdc, 0xe9, 0x37, 0x1c, 0x97, 0xfb, 0x9c, 0x65, 0x17, 0xe9, 0xff, 0xd4, 0xa0, 0xbc, 0x65, - 0xdb, 0x8c, 0xf8, 0xfe, 0x2e, 0xa3, 0x43, 0x0f, 0x7d, 0x0f, 0x16, 0xc5, 0x4e, 0x6c, 0x93, 0x9b, - 0x55, 0x6d, 0x5d, 0xdb, 0x58, 0xda, 0x7c, 0xcd, 0x08, 0x14, 0x1b, 0x49, 0xc5, 0xb1, 0x87, 0x84, - 0xb4, 0x71, 0xf2, 0xc0, 0x78, 0x7a, 0xf8, 0x7d, 0x62, 0xf1, 0x27, 0x84, 0x9b, 0x4d, 0xf4, 0x62, - 0x54, 0x9f, 0x1b, 0x8f, 0xea, 0x10, 0x8f, 0xe1, 0x48, 0x2b, 0xfa, 0x89, 0x06, 0xe5, 0x9e, 0xc0, - 0x7a, 0x42, 0x06, 0x87, 0x84, 0xf9, 0xd5, 0xdc, 0x7a, 0x7e, 0x63, 0x69, 0xb3, 0x65, 0x4c, 0x13, - 0x13, 0xc6, 0x6e, 0xac, 0xb1, 0xf9, 0x8a, 0xc2, 0x2f, 0x27, 0x06, 0x7d, 0x9c, 0x02, 0xd5, 0x5f, - 0x6a, 0x50, 0x49, 0x6e, 0x7c, 0xcf, 0xf1, 0x39, 0xfa, 0xce, 0xc4, 0xe6, 0x8d, 0xab, 0x6d, 0x5e, - 0xac, 0x96, 0x5b, 0xaf, 0x28, 0xe8, 0xc5, 0x70, 0x24, 0xb1, 0x71, 0x0a, 0x45, 0x87, 0x93, 0x41, - 0xb8, 0xe1, 0x6f, 0x4e, 0xb7, 0xe1, 0x24, 0xf9, 0xe6, 0xb2, 0x82, 0x2d, 0xb6, 0x04, 0x00, 0x0e, - 0x70, 0xf4, 0x77, 0xf3, 0xb0, 0x9a, 0x14, 0xeb, 0x98, 0xdc, 0x3a, 0xba, 0x01, 0x0f, 0xff, 0x4a, - 0x83, 0x55, 0xd3, 0xb6, 0x89, 0xbd, 0x7b, 0xad, 0x6e, 0xfe, 0xa4, 0x22, 0x21, 0xf6, 0x98, 0xc6, - 0xc2, 0x93, 0xf0, 0xe8, 0x37, 0x1a, 0xac, 0x31, 0x32, 0xa0, 0x27, 0x19, 0x5a, 0xf9, 0x59, 0xd3, - 0xfa, 0x94, 0xa2, 0xb5, 0x86, 0x27, 0xd1, 0xf0, 0x79, 0x14, 0xf4, 0x7f, 0x69, 0xb0, 0xb2, 0xe5, - 0x79, 0x7d, 0x87, 0xd8, 0x07, 0xf4, 0x7f, 0x2b, 0x0d, 0xff, 0xaa, 0x01, 0x4a, 0x6f, 0xfd, 0x06, - 0x12, 0xf1, 0xcd, 0x74, 0x22, 0xee, 0x4d, 0x99, 0x88, 0x29, 0xfa, 0x17, 0xa4, 0xe2, 0x7b, 0x79, - 0x58, 0x4b, 0x0b, 0xfe, 0x3f, 0x19, 0xff, 0x3b, 0x93, 0xf1, 0x9d, 0x1c, 0xac, 0x3d, 0xee, 0x0f, - 0x7d, 0x4e, 0x58, 0x8a, 0xf2, 0xf5, 0x7b, 0xea, 0xe7, 0x1a, 0x54, 0x48, 0xb7, 0x4b, 0x2c, 0xee, - 0x9c, 0x90, 0x6b, 0x73, 0x54, 0x55, 0x71, 0xa8, 0xec, 0x64, 0xa0, 0xf0, 0x04, 0xb8, 0xde, 0x87, - 0x3b, 0x3b, 0xcf, 0x39, 0x61, 0xae, 0xd9, 0xdf, 0x71, 0xb9, 0xc3, 0x4f, 0x31, 0xe9, 0x12, 0x46, - 0x5c, 0x8b, 0xa0, 0x75, 0x28, 0xb8, 0xe6, 0x80, 0x48, 0x53, 0x94, 0x9a, 0x65, 0xa5, 0xb4, 0xd0, - 0x36, 0x07, 0x04, 0xcb, 0x19, 0xd4, 0x80, 0x92, 0xf8, 0xeb, 0x7b, 0xa6, 0x45, 0xaa, 0x39, 0x29, - 0xb6, 0xaa, 0xc4, 0x4a, 0xed, 0x70, 0x02, 0xc7, 0x32, 0xc2, 0xf2, 0x15, 0xc9, 0x74, 0xcb, 0xf7, - 0xa9, 0xe5, 0x98, 0xdc, 0xa1, 0xee, 0xcd, 0x24, 0x48, 0xc5, 0x54, 0x88, 0x2a, 0x16, 0x66, 0x54, - 0x19, 0xa4, 0xae, 0xc8, 0x64, 0xb1, 0xe5, 0xb7, 0x32, 0x68, 0x78, 0x02, 0x5f, 0xff, 0x6d, 0x1e, - 0x96, 0x12, 0x5e, 0x43, 0x04, 0xf2, 0x1e, 0xb5, 0x95, 0x05, 0xa6, 0xec, 0x1c, 0x3a, 0xd4, 0x8e, - 0x49, 0x2d, 0x8c, 0x47, 0xf5, 0xbc, 0x18, 0x11, 0xfa, 0xd1, 0x2f, 0x35, 0x58, 0x21, 0x29, 0x8f, - 0x4b, 0xcf, 0x2d, 0x6d, 0x3e, 0x9b, 0x0e, 0xf2, 0x82, 0x28, 0x6a, 0xa2, 0xf1, 0xa8, 0xbe, 0x92, - 0x99, 0xcc, 0x10, 0x40, 0x9f, 0x87, 0xbc, 0xe3, 0x05, 0xa5, 0xa1, 0xdc, 0x7c, 0x45, 0xd0, 0x6d, - 0x75, 0xfc, 0xb3, 0x51, 0xbd, 0xd4, 0xea, 0xa8, 0xe6, 0x06, 0x0b, 0x01, 0xd4, 0x87, 0xa2, 0x47, - 0x19, 0xf7, 0xab, 0x05, 0xe9, 0xbb, 0xdd, 0xe9, 0x18, 0x8b, 0x08, 0xb5, 0x3b, 0x94, 0xf1, 0xb8, - 0xa0, 0x8b, 0x2f, 0x1f, 0x07, 0x20, 0xfa, 0x7b, 0x1a, 0xac, 0xa4, 0xfd, 0x9b, 0x0e, 0x78, 0xed, - 0xf2, 0x80, 0x8f, 0x72, 0x28, 0x77, 0x61, 0x0e, 0x35, 0x21, 0x3f, 0x74, 0xec, 0x6a, 0x5e, 0x0a, - 0xbc, 0xa6, 0x04, 0xf2, 0xcf, 0x5a, 0xdb, 0x67, 0xa3, 0xfa, 0xab, 0x17, 0xf5, 0xfb, 0xfc, 0xd4, - 0x23, 0xbe, 0xf1, 0xac, 0xb5, 0x8d, 0xc5, 0x62, 0xfd, 0xcf, 0x1a, 0x2c, 0xb4, 0x3a, 0xcd, 0x3e, - 0xb5, 0x8e, 0x11, 0x81, 0x82, 0xe5, 0xd8, 0x4c, 0xc5, 0xd1, 0xe3, 0xe9, 0x4c, 0xd4, 0xea, 0xb4, - 0x09, 0x8f, 0x69, 0x3f, 0x6e, 0x6d, 0x63, 0x2c, 0xd5, 0xa3, 0x63, 0x98, 0x27, 0xcf, 0x2d, 0xe2, - 0x71, 0x95, 0x47, 0x33, 0x01, 0x5a, 0x51, 0x40, 0xf3, 0x3b, 0x52, 0x35, 0x56, 0x10, 0x7a, 0x17, - 0x8a, 0x52, 0x00, 0x7d, 0x06, 0x72, 0x8e, 0x27, 0xb7, 0x56, 0x6e, 0xae, 0x8d, 0x47, 0xf5, 0x5c, - 0xab, 0x93, 0x0e, 0x93, 0x9c, 0xe3, 0xa1, 0x47, 0x50, 0xf6, 0x18, 0xe9, 0x3a, 0xcf, 0xf7, 0x88, - 0xdb, 0xe3, 0x47, 0xd2, 0xf6, 0xc5, 0xb8, 0x55, 0xe9, 0x24, 0xe6, 0x70, 0x4a, 0x52, 0x7f, 0x47, - 0x83, 0x52, 0x14, 0x15, 0xc2, 0x77, 0x22, 0x10, 0x24, 0x5c, 0x31, 0x36, 0x82, 0x98, 0xc3, 0x72, - 0xe6, 0x0a, 0xde, 0x7d, 0x04, 0x8b, 0xf2, 0x16, 0x66, 0xd1, 0xbe, 0x72, 0xf1, 0xbd, 0xb0, 0x6b, - 0xe9, 0xa8, 0xf1, 0xb3, 0xc4, 0x6f, 0x1c, 0x49, 0xeb, 0x3f, 0x2d, 0xc0, 0x72, 0x9b, 0xf0, 0xb7, - 0x28, 0x3b, 0xee, 0xd0, 0xbe, 0x63, 0x9d, 0xde, 0x40, 0x9d, 0xe4, 0x50, 0x64, 0xc3, 0x3e, 0x09, - 0x6b, 0xe3, 0xd3, 0x29, 0xf3, 0x2b, 0xc9, 0x1e, 0x0f, 0xfb, 0x24, 0xce, 0x33, 0xf1, 0xe5, 0xe3, - 0x00, 0x0c, 0x7d, 0x0d, 0x6e, 0x99, 0xa9, 0xbe, 0x29, 0xa8, 0x04, 0x25, 0xe9, 0xe1, 0x5b, 0xe9, - 0x96, 0xca, 0xc7, 0x59, 0x59, 0xb4, 0x21, 0x4c, 0xec, 0x50, 0x26, 0x2a, 0x59, 0x61, 0x5d, 0xdb, - 0xd0, 0x9a, 0xe5, 0xc0, 0xbc, 0xc1, 0x18, 0x8e, 0x66, 0xd1, 0x43, 0x28, 0x73, 0x87, 0xb0, 0x70, - 0xa6, 0x5a, 0x94, 0x8e, 0xad, 0x88, 0xa0, 0x38, 0x48, 0x8c, 0xe3, 0x94, 0x14, 0xfa, 0xb1, 0x06, - 0x25, 0x9f, 0x0e, 0x99, 0x45, 0x30, 0xe9, 0x56, 0xe7, 0xa5, 0xe1, 0x0f, 0x66, 0x69, 0x99, 0xa8, - 0x54, 0x2e, 0x8b, 0x32, 0xb2, 0x1f, 0x42, 0xe1, 0x18, 0x55, 0xff, 0x50, 0x83, 0xd5, 0xd4, 0xa2, - 0x1b, 0x68, 0xa1, 0xbd, 0x74, 0x0b, 0xfd, 0xad, 0x19, 0x6e, 0xf9, 0x82, 0x0e, 0xfa, 0x07, 0x70, - 0x27, 0x25, 0xd6, 0xa6, 0x36, 0xd9, 0xe7, 0x26, 0x1f, 0xfa, 0xe8, 0x0b, 0xb0, 0xe8, 0x52, 0x9b, - 0xb4, 0xe3, 0x7e, 0x24, 0xa2, 0xde, 0x56, 0xe3, 0x38, 0x92, 0x40, 0x9b, 0x00, 0xea, 0x15, 0xc4, - 0xa1, 0xae, 0xcc, 0xce, 0x7c, 0x1c, 0xf9, 0xbb, 0xd1, 0x0c, 0x4e, 0x48, 0xe9, 0x7f, 0xcc, 0x9a, - 0xb8, 0x43, 0x08, 0x43, 0x5f, 0x81, 0x65, 0x33, 0x71, 0xbd, 0xf6, 0xab, 0x9a, 0x8c, 0xcc, 0xd5, - 0xf1, 0xa8, 0xbe, 0x9c, 0xbc, 0x77, 0xfb, 0x38, 0x2d, 0x87, 0x7c, 0x58, 0x74, 0x3c, 0x59, 0x91, - 0x43, 0x03, 0xee, 0x4c, 0x5b, 0x21, 0xa5, 0xb6, 0x78, 0xdf, 0x6a, 0xc0, 0xc7, 0x11, 0x90, 0xfe, - 0x37, 0x0d, 0x3e, 0x71, 0x7e, 0x6c, 0xa1, 0x2f, 0x41, 0x41, 0x1c, 0x1a, 0xca, 0x78, 0xaf, 0x86, - 0xa5, 0xea, 0xe0, 0xd4, 0x23, 0x67, 0xa3, 0x7a, 0x7a, 0xe7, 0x62, 0x10, 0x4b, 0xf1, 0x8f, 0xdd, - 0xe1, 0x45, 0x25, 0x31, 0x7f, 0xd9, 0x81, 0x57, 0x98, 0xe6, 0xc0, 0xfb, 0x53, 0x31, 0xe3, 0x2c, - 0x51, 0x50, 0xd0, 0xeb, 0x50, 0xb2, 0x1d, 0x26, 0xfa, 0x5b, 0xea, 0xaa, 0x8d, 0xd6, 0x42, 0xb2, - 0xdb, 0xe1, 0xc4, 0x59, 0xf2, 0x03, 0xc7, 0x0b, 0xd0, 0x9b, 0x50, 0xe8, 0x32, 0x3a, 0x50, 0xdd, - 0xd0, 0x2c, 0x6b, 0x9f, 0x88, 0xa4, 0xd8, 0x14, 0x6f, 0x30, 0x3a, 0xc0, 0x12, 0x0a, 0x1d, 0x43, - 0x8e, 0x53, 0x69, 0xaa, 0x6b, 0x00, 0x04, 0x05, 0x98, 0x3b, 0xa0, 0x38, 0xc7, 0xa9, 0x88, 0x48, - 0x9f, 0xb0, 0x13, 0xc7, 0x22, 0x61, 0xff, 0x34, 0x65, 0x44, 0xee, 0x07, 0xda, 0xe2, 0x88, 0x54, - 0x03, 0x3e, 0x8e, 0x80, 0x44, 0xde, 0x7a, 0x99, 0x72, 0x1b, 0x9f, 0x7f, 0x13, 0x05, 0x9a, 0xc0, - 0xbc, 0x19, 0x78, 0x6f, 0x5e, 0x7a, 0xef, 0x89, 0xe8, 0x05, 0xb6, 0x42, 0xb7, 0x7d, 0xfd, 0xca, - 0xef, 0xd3, 0xcc, 0x8e, 0x9e, 0x78, 0x0d, 0x11, 0x19, 0x81, 0x0a, 0xac, 0x94, 0xa3, 0xaf, 0xc2, - 0x32, 0x71, 0xcd, 0xc3, 0x3e, 0xd9, 0xa3, 0xbd, 0x9e, 0xe3, 0xf6, 0xaa, 0x0b, 0xeb, 0xda, 0xc6, - 0x62, 0xf3, 0xb6, 0x62, 0xb6, 0xbc, 0x93, 0x9c, 0xc4, 0x69, 0xd9, 0xf3, 0x4e, 0xab, 0xc5, 0x8f, - 0x71, 0x5a, 0x85, 0xf9, 0x51, 0xba, 0x28, 0x3f, 0xf4, 0xdf, 0xe7, 0x01, 0xa5, 0xbc, 0x29, 0x4a, - 0xa0, 0x2f, 0xfa, 0xf6, 0x65, 0x37, 0x39, 0xac, 0x4a, 0xfe, 0xf5, 0x1c, 0x45, 0x91, 0x2d, 0xd2, - 0xf3, 0x69, 0x06, 0xe8, 0x87, 0x50, 0xe6, 0xcc, 0xec, 0x76, 0x1d, 0x4b, 0x72, 0x54, 0xa9, 0xb3, - 0x7d, 0x65, 0x46, 0xf2, 0x35, 0xde, 0x88, 0x5c, 0x75, 0x90, 0xd0, 0x15, 0xf7, 0x6b, 0xc9, 0x51, - 0x9c, 0xc2, 0x43, 0x3f, 0xd3, 0xa0, 0x22, 0x7a, 0x88, 0xa4, 0x88, 0x7a, 0x60, 0xf8, 0xc6, 0x7f, - 0x4a, 0x02, 0x67, 0xf4, 0xc5, 0x77, 0xba, 0xec, 0x0c, 0x9e, 0xc0, 0xd6, 0xff, 0xa1, 0xc1, 0xda, - 0x84, 0xef, 0x86, 0x37, 0xf1, 0xb2, 0xf0, 0x36, 0x14, 0xc5, 0xf1, 0x17, 0x1e, 0x36, 0xcf, 0x66, - 0x18, 0x15, 0xf1, 0x31, 0x1c, 0x9f, 0xdb, 0x62, 0xcc, 0xc7, 0x01, 0xa4, 0xfe, 0xf7, 0x02, 0x54, - 0x42, 0x21, 0x7f, 0x7f, 0x38, 0x18, 0x98, 0xec, 0x26, 0xba, 0xd5, 0x5f, 0x6b, 0x70, 0x2b, 0x19, - 0x8f, 0x4e, 0xb4, 0xfb, 0xce, 0x0c, 0x77, 0x1f, 0x04, 0xc1, 0x1d, 0xc5, 0xe4, 0x56, 0x3b, 0x0d, - 0x88, 0xb3, 0x0c, 0xd0, 0x1f, 0x34, 0xb8, 0x17, 0xa0, 0xa8, 0x27, 0xa6, 0xcc, 0x0a, 0x15, 0x9f, - 0xb3, 0xa7, 0xf8, 0x59, 0x45, 0xf1, 0xde, 0xd6, 0x47, 0xa0, 0xe3, 0x8f, 0xe4, 0x86, 0xde, 0xd5, - 0xe0, 0x76, 0x20, 0x90, 0x65, 0x5d, 0xb8, 0x26, 0xd6, 0x9f, 0x56, 0xac, 0x6f, 0x6f, 0x9d, 0x07, - 0x8b, 0xcf, 0x67, 0xa3, 0x9b, 0x50, 0x4e, 0x3e, 0x71, 0x5c, 0xc7, 0x53, 0xd5, 0xef, 0x34, 0x58, - 0x50, 0x07, 0x1a, 0x7a, 0x98, 0xb8, 0xc5, 0x05, 0x10, 0xd5, 0xcb, 0x6f, 0x70, 0xa8, 0xad, 0xee, - 0x8f, 0xb9, 0x4b, 0xa2, 0x7f, 0xc8, 0x9d, 0xbe, 0x11, 0xfc, 0xf3, 0xce, 0x68, 0xb9, 0xfc, 0x29, - 0xdb, 0xe7, 0xcc, 0x71, 0x7b, 0xcd, 0xc5, 0xcc, 0x6d, 0xf3, 0x73, 0xb0, 0x40, 0x5c, 0x79, 0x35, - 0x95, 0x2d, 0x43, 0xb1, 0xb9, 0x34, 0x1e, 0xd5, 0x17, 0x76, 0x82, 0x21, 0x1c, 0xce, 0xe9, 0x04, - 0x2a, 0x8a, 0xf7, 0x75, 0xda, 0xa7, 0x79, 0xff, 0xc5, 0xcb, 0xda, 0xdc, 0xfb, 0x2f, 0x6b, 0x73, - 0x1f, 0xbc, 0xac, 0xcd, 0xfd, 0x68, 0x5c, 0xd3, 0x5e, 0x8c, 0x6b, 0xda, 0xfb, 0xe3, 0x9a, 0xf6, - 0xc1, 0xb8, 0xa6, 0xfd, 0x65, 0x5c, 0xd3, 0x7e, 0xf1, 0x61, 0x6d, 0xee, 0xdb, 0x0b, 0xca, 0xf5, - 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x52, 0x8e, 0x3c, 0xbb, 0x5d, 0x1e, 0x00, 0x00, + 0x19, 0x4f, 0xf9, 0x91, 0xc4, 0x5f, 0x9c, 0xc4, 0xa9, 0xec, 0x30, 0x66, 0x18, 0xec, 0x6c, 0xf3, + 0x50, 0x0e, 0x4c, 0x7b, 0x27, 0x0c, 0x30, 0x12, 0x8b, 0x50, 0x3c, 0xc9, 0x06, 0x43, 0xc6, 0x63, + 0x55, 0x32, 0x17, 0x84, 0x04, 0x9d, 0xee, 0xb2, 0xd3, 0xc4, 0xee, 0xea, 0xed, 0x2e, 0x67, 0x27, + 0x8b, 0x84, 0x40, 0x9c, 0x96, 0x03, 0xcf, 0x0b, 0x27, 0x6e, 0xab, 0xe5, 0x2f, 0x40, 0x82, 0x1b, + 0xb7, 0x39, 0xee, 0x71, 0x39, 0x60, 0x88, 0x57, 0x70, 0xe5, 0x80, 0x40, 0x28, 0x27, 0x54, 0xd5, + 0xe5, 0x7e, 0x39, 0xd9, 0x64, 0xb0, 0x13, 0xad, 0xb4, 0x7b, 0x4a, 0xba, 0xea, 0xab, 0xef, 0xf7, + 0xfb, 0xea, 0x7b, 0x55, 0x95, 0x61, 0xb7, 0x63, 0xf3, 0xc3, 0xfe, 0x81, 0x6e, 0xb2, 0x5e, 0xed, + 0xb8, 0xf7, 0x86, 0xe1, 0xd1, 0x7b, 0xdc, 0x70, 0xde, 0xec, 0xd7, 0x0c, 0x87, 0x7b, 0xd4, 0xa8, + 0xb9, 0x47, 0x9d, 0x9a, 0xe1, 0xda, 0x7e, 0xcd, 0x64, 0x0e, 0xf7, 0x58, 0xd7, 0xed, 0x1a, 0x0e, + 0xad, 0x1d, 0xdf, 0x3f, 0xa0, 0xdc, 0xd8, 0xa8, 0x75, 0xa8, 0x43, 0x3d, 0x83, 0x53, 0x4b, 0x77, + 0x3d, 0xc6, 0x19, 0x7e, 0x35, 0xd2, 0xa6, 0x07, 0xda, 0xbe, 0x2b, 0xb5, 0xe9, 0x81, 0x36, 0xdd, + 0x3d, 0xea, 0xe8, 0x42, 0x9b, 0x1e, 0xd7, 0xa6, 0x2b, 0x6d, 0x77, 0xee, 0xc5, 0xb8, 0x74, 0x58, + 0x87, 0xd5, 0xa4, 0xd2, 0x83, 0x7e, 0x5b, 0x7e, 0xc9, 0x0f, 0xf9, 0x5f, 0x00, 0x76, 0xe7, 0xb5, + 0xab, 0x52, 0xf7, 0xb9, 0xc1, 0xfd, 0xda, 0xf1, 0x7d, 0xa3, 0xeb, 0x1e, 0x1a, 0xf7, 0xd3, 0xa4, + 0xef, 0x3c, 0x38, 0x7a, 0xe8, 0xeb, 0x36, 0x13, 0xb2, 0x3d, 0xc3, 0x3c, 0xb4, 0x1d, 0xea, 0x9d, + 0x44, 0x8b, 0x7b, 0x94, 0x1b, 0xb5, 0xe3, 0xf1, 0x55, 0xb5, 0x8b, 0x56, 0x79, 0x7d, 0x87, 0xdb, + 0x3d, 0x3a, 0xb6, 0xe0, 0xcb, 0x97, 0x2d, 0xf0, 0xcd, 0x43, 0xda, 0x33, 0xc6, 0xd6, 0x7d, 0xf1, + 0xa2, 0x75, 0x7d, 0x6e, 0x77, 0x6b, 0xb6, 0xc3, 0x7d, 0xee, 0xa5, 0x17, 0x69, 0xff, 0x41, 0x50, + 0xdc, 0xb4, 0x2c, 0x8f, 0xfa, 0xfe, 0x8e, 0xc7, 0xfa, 0x2e, 0xfe, 0x1e, 0xcc, 0x0b, 0x4b, 0x2c, + 0x83, 0x1b, 0x65, 0xb4, 0x86, 0xd6, 0x17, 0x36, 0x5e, 0xd1, 0x03, 0xc5, 0x7a, 0x5c, 0x71, 0xe4, + 0x21, 0x21, 0xad, 0x1f, 0xdf, 0xd7, 0x9f, 0x1c, 0x7c, 0x9f, 0x9a, 0xfc, 0x31, 0xe5, 0x46, 0x1d, + 0x3f, 0x1f, 0x54, 0x67, 0x86, 0x83, 0x2a, 0x44, 0x63, 0x24, 0xd4, 0x8a, 0x7f, 0x82, 0xa0, 0xd8, + 0x11, 0x58, 0x8f, 0x69, 0xef, 0x80, 0x7a, 0x7e, 0x39, 0xb3, 0x96, 0x5d, 0x5f, 0xd8, 0x68, 0xe8, + 0x93, 0xc4, 0x84, 0xbe, 0x13, 0x69, 0xac, 0xbf, 0xa4, 0xf0, 0x8b, 0xb1, 0x41, 0x9f, 0x24, 0x40, + 0xb5, 0x53, 0x04, 0xa5, 0xb8, 0xe1, 0xbb, 0xb6, 0xcf, 0xf1, 0x77, 0xc6, 0x8c, 0xd7, 0xaf, 0x66, + 0xbc, 0x58, 0x2d, 0x4d, 0x2f, 0x29, 0xe8, 0xf9, 0xd1, 0x48, 0xcc, 0x70, 0x06, 0x79, 0x9b, 0xd3, + 0xde, 0xc8, 0xe0, 0x6f, 0x4e, 0x66, 0x70, 0x9c, 0x7c, 0x7d, 0x51, 0xc1, 0xe6, 0x1b, 0x02, 0x80, + 0x04, 0x38, 0xda, 0xdb, 0x59, 0x58, 0x89, 0x8b, 0xb5, 0x0c, 0x6e, 0x1e, 0xde, 0x80, 0x87, 0x7f, + 0x85, 0x60, 0xc5, 0xb0, 0x2c, 0x6a, 0xed, 0x5c, 0xab, 0x9b, 0x3f, 0xa9, 0x48, 0x08, 0x1b, 0x93, + 0x58, 0x64, 0x1c, 0x1e, 0xff, 0x06, 0xc1, 0xaa, 0x47, 0x7b, 0xec, 0x38, 0x45, 0x2b, 0x3b, 0x6d, + 0x5a, 0x9f, 0x52, 0xb4, 0x56, 0xc9, 0x38, 0x1a, 0x39, 0x8f, 0x82, 0xf6, 0x5f, 0x04, 0x4b, 0x9b, + 0xae, 0xdb, 0xb5, 0xa9, 0xb5, 0xcf, 0x3e, 0x5a, 0x69, 0xf8, 0x77, 0x04, 0x38, 0x69, 0xfa, 0x0d, + 0x24, 0xe2, 0xeb, 0xc9, 0x44, 0xdc, 0x9d, 0x30, 0x11, 0x13, 0xf4, 0x2f, 0x48, 0xc5, 0x77, 0xb2, + 0xb0, 0x9a, 0x14, 0xfc, 0x38, 0x19, 0x3f, 0x9c, 0xc9, 0xf8, 0x56, 0x06, 0x56, 0x1f, 0x75, 0xfb, + 0x3e, 0xa7, 0x5e, 0x82, 0xf2, 0xf5, 0x7b, 0xea, 0xe7, 0x08, 0x4a, 0xb4, 0xdd, 0xa6, 0x26, 0xb7, + 0x8f, 0xe9, 0xb5, 0x39, 0xaa, 0xac, 0x38, 0x94, 0xb6, 0x53, 0x50, 0x64, 0x0c, 0x5c, 0xfb, 0x37, + 0x82, 0x85, 0xed, 0xce, 0x47, 0xef, 0x70, 0xf0, 0x57, 0x04, 0xcb, 0x31, 0xbb, 0x6f, 0xa0, 0x24, + 0x39, 0xc9, 0x92, 0x34, 0xa1, 0xbd, 0x31, 0xee, 0x17, 0xd4, 0xa3, 0xdf, 0x65, 0xa1, 0x14, 0x93, + 0x0a, 0x8a, 0x91, 0x05, 0xc0, 0x42, 0xa7, 0x4c, 0xd5, 0xc1, 0x31, 0xbd, 0x1f, 0x17, 0xa4, 0x17, + 0x2c, 0x48, 0x5d, 0xb8, 0xbd, 0xfd, 0x8c, 0x53, 0xcf, 0x31, 0xba, 0xdb, 0x0e, 0xb7, 0xf9, 0x09, + 0xa1, 0x6d, 0xea, 0x51, 0xc7, 0xa4, 0x78, 0x0d, 0x72, 0x8e, 0xd1, 0xa3, 0xd2, 0x55, 0x85, 0x7a, + 0x51, 0xa9, 0xce, 0x35, 0x8d, 0x1e, 0x25, 0x72, 0x06, 0xd7, 0xa0, 0x20, 0xfe, 0xfa, 0xae, 0x61, + 0xd2, 0x72, 0x46, 0x8a, 0xad, 0x28, 0xb1, 0x42, 0x73, 0x34, 0x41, 0x22, 0x19, 0x51, 0xfe, 0x4a, + 0x12, 0x7e, 0xd3, 0xf7, 0x99, 0x69, 0x1b, 0xdc, 0x66, 0xce, 0xcd, 0x74, 0xa9, 0x92, 0xa1, 0x10, + 0x95, 0xfd, 0x53, 0x6a, 0xcf, 0x52, 0x57, 0xb8, 0x65, 0x51, 0xf9, 0xdb, 0x4c, 0xa1, 0x91, 0x31, + 0x7c, 0xed, 0xb7, 0x59, 0x58, 0x88, 0xb9, 0x02, 0x53, 0xc8, 0xba, 0xcc, 0x52, 0x3b, 0x30, 0xe1, + 0xf1, 0xbd, 0xc5, 0xac, 0x88, 0xd4, 0xdc, 0x70, 0x50, 0xcd, 0x8a, 0x11, 0xa1, 0x1f, 0xff, 0x12, + 0xc1, 0x12, 0x4d, 0x78, 0x5c, 0x7a, 0x6e, 0x61, 0xe3, 0xe9, 0x84, 0x55, 0xe1, 0xfc, 0x28, 0xaa, + 0xe3, 0xe1, 0xa0, 0xba, 0x94, 0x9a, 0x4c, 0x11, 0xc0, 0x9f, 0x87, 0xac, 0xed, 0x06, 0xe9, 0x50, + 0xac, 0xbf, 0x24, 0xe8, 0x36, 0x5a, 0xfe, 0xd9, 0xa0, 0x5a, 0x68, 0xb4, 0xd4, 0x0d, 0x83, 0x08, + 0x01, 0xdc, 0x85, 0xbc, 0xcb, 0x3c, 0xee, 0x97, 0x73, 0xd2, 0x77, 0x3b, 0x93, 0x31, 0x16, 0x11, + 0x6a, 0xb5, 0x98, 0xc7, 0xa3, 0x2a, 0x26, 0xbe, 0x7c, 0x12, 0x80, 0x68, 0xef, 0x20, 0x58, 0x4a, + 0xfa, 0x37, 0x19, 0xf0, 0xe8, 0xf2, 0x80, 0x0f, 0x73, 0x28, 0x73, 0x61, 0x0e, 0xd5, 0x21, 0xdb, + 0xb7, 0xad, 0x72, 0x56, 0x0a, 0xbc, 0xa2, 0x04, 0xb2, 0x4f, 0x1b, 0x5b, 0x67, 0x83, 0xea, 0xcb, + 0x17, 0x5d, 0xba, 0xf9, 0x89, 0x4b, 0x7d, 0xfd, 0x69, 0x63, 0x8b, 0x88, 0xc5, 0xda, 0x5f, 0x10, + 0xcc, 0x35, 0x5a, 0xf5, 0x2e, 0x33, 0x8f, 0x30, 0x85, 0x9c, 0x69, 0x5b, 0x9e, 0x8a, 0xa3, 0x47, + 0x93, 0x6d, 0x51, 0xa3, 0xd5, 0xa4, 0x3c, 0xa2, 0xfd, 0xa8, 0xb1, 0x45, 0x88, 0x54, 0x8f, 0x8f, + 0x60, 0x96, 0x3e, 0x33, 0xa9, 0xcb, 0x55, 0x1e, 0x4d, 0x05, 0x68, 0x49, 0x01, 0xcd, 0x6e, 0x4b, + 0xd5, 0x44, 0x41, 0x68, 0x6d, 0xc8, 0x4b, 0x01, 0xfc, 0x19, 0xc8, 0xd8, 0xae, 0x34, 0xad, 0x58, + 0x5f, 0x1d, 0x0e, 0xaa, 0x99, 0x46, 0x2b, 0x19, 0x26, 0x19, 0xdb, 0xc5, 0x0f, 0xa1, 0xe8, 0x7a, + 0xb4, 0x6d, 0x3f, 0xdb, 0xa5, 0x4e, 0x87, 0x1f, 0xca, 0xbd, 0xcf, 0x47, 0x9d, 0xb9, 0x15, 0x9b, + 0x23, 0x09, 0x49, 0xed, 0x2d, 0x04, 0x85, 0x30, 0x2a, 0x84, 0xef, 0x44, 0x20, 0x48, 0xb8, 0x7c, + 0xb4, 0x09, 0x62, 0x8e, 0xc8, 0x99, 0x2b, 0x78, 0xf7, 0x21, 0xcc, 0xcb, 0xa7, 0x10, 0x93, 0x75, + 0x95, 0x8b, 0xef, 0x8e, 0xfa, 0x74, 0x4b, 0x8d, 0x9f, 0xc5, 0xfe, 0x27, 0xa1, 0xb4, 0xf6, 0xd3, + 0x1c, 0x2c, 0x36, 0x29, 0x7f, 0x83, 0x79, 0x47, 0x2d, 0xd6, 0xb5, 0xcd, 0x93, 0x1b, 0xa8, 0x93, + 0x1c, 0xf2, 0x5e, 0xbf, 0x4b, 0x47, 0xb5, 0xf1, 0xc9, 0x84, 0xf9, 0x15, 0x67, 0x4f, 0xfa, 0x5d, + 0x1a, 0xe5, 0x99, 0xf8, 0xf2, 0x49, 0x00, 0x86, 0xbf, 0x06, 0xcb, 0x46, 0xe2, 0xf2, 0x12, 0x54, + 0x82, 0x82, 0xf4, 0xf0, 0x72, 0xf2, 0x5e, 0xe3, 0x93, 0xb4, 0x2c, 0x5e, 0x17, 0x5b, 0x6c, 0x33, + 0x4f, 0x54, 0xb2, 0xdc, 0x1a, 0x5a, 0x47, 0xf5, 0x62, 0xb0, 0xbd, 0xc1, 0x18, 0x09, 0x67, 0xf1, + 0x03, 0x28, 0x72, 0x9b, 0x7a, 0xa3, 0x99, 0x72, 0x5e, 0x3a, 0xb6, 0x24, 0x82, 0x62, 0x3f, 0x36, + 0x4e, 0x12, 0x52, 0xf8, 0xc7, 0x08, 0x0a, 0x3e, 0xeb, 0x7b, 0x26, 0x25, 0xb4, 0x5d, 0x9e, 0x95, + 0x1b, 0xbf, 0x3f, 0xcd, 0x9d, 0x09, 0x4b, 0xe5, 0xa2, 0x28, 0x23, 0x7b, 0x23, 0x28, 0x12, 0xa1, + 0x6a, 0xef, 0x23, 0x58, 0x49, 0x2c, 0xba, 0x81, 0x43, 0xa3, 0x9b, 0x3c, 0x34, 0x7e, 0x6b, 0x8a, + 0x26, 0x5f, 0x70, 0x6c, 0xfc, 0x01, 0xdc, 0x4e, 0x88, 0x35, 0x99, 0x45, 0xf7, 0xb8, 0xc1, 0xfb, + 0x3e, 0xfe, 0x02, 0xcc, 0x3b, 0xcc, 0xa2, 0xcd, 0xe8, 0x3c, 0x12, 0x52, 0x6f, 0xaa, 0x71, 0x12, + 0x4a, 0xe0, 0x0d, 0x00, 0xf5, 0x14, 0x69, 0x33, 0x47, 0x66, 0x67, 0x36, 0x8a, 0xfc, 0x9d, 0x70, + 0x86, 0xc4, 0xa4, 0xb4, 0x3f, 0xa5, 0xb7, 0xb8, 0x45, 0xa9, 0x87, 0xbf, 0x02, 0x8b, 0x46, 0xec, + 0x8d, 0xcb, 0x2f, 0x23, 0x19, 0x99, 0x2b, 0xc3, 0x41, 0x75, 0x31, 0xfe, 0xf8, 0xe5, 0x93, 0xa4, + 0x1c, 0xf6, 0x61, 0xde, 0x76, 0x65, 0x45, 0x1e, 0x6d, 0xe0, 0xf6, 0xa4, 0x15, 0x52, 0x6a, 0x8b, + 0xec, 0x56, 0x03, 0x3e, 0x09, 0x81, 0xb4, 0x7f, 0x20, 0xf8, 0xc4, 0xf9, 0xb1, 0x85, 0xbf, 0x04, + 0x39, 0xd1, 0x34, 0xd4, 0xe6, 0xbd, 0x3c, 0x2a, 0x55, 0xfb, 0x27, 0x2e, 0x3d, 0x1b, 0x54, 0x93, + 0x96, 0x8b, 0x41, 0x22, 0xc5, 0x5f, 0xf8, 0x84, 0x17, 0x96, 0xc4, 0xec, 0x65, 0x0d, 0x2f, 0x37, + 0x49, 0xc3, 0xfb, 0x73, 0x3e, 0xe5, 0x2c, 0x51, 0x50, 0xf0, 0xab, 0x50, 0xb0, 0x6c, 0x4f, 0x5c, + 0x32, 0x99, 0xa3, 0x0c, 0xad, 0x8c, 0xc8, 0x6e, 0x8d, 0x26, 0xce, 0xe2, 0x1f, 0x24, 0x5a, 0x80, + 0x5f, 0x87, 0x5c, 0xdb, 0x63, 0x3d, 0x75, 0x1a, 0x9a, 0x66, 0xed, 0x13, 0x91, 0x14, 0x6d, 0xc5, + 0x6b, 0x1e, 0xeb, 0x11, 0x09, 0x85, 0x8f, 0x20, 0xc3, 0x99, 0xdc, 0xaa, 0x6b, 0x00, 0x04, 0x05, + 0x98, 0xd9, 0x67, 0x24, 0xc3, 0x99, 0x88, 0x48, 0x9f, 0x7a, 0xc7, 0xb6, 0x49, 0x47, 0xe7, 0xa7, + 0x09, 0x23, 0x72, 0x2f, 0xd0, 0x16, 0x45, 0xa4, 0x1a, 0xf0, 0x49, 0x08, 0x24, 0xf2, 0xd6, 0x4d, + 0x95, 0xdb, 0xa8, 0xff, 0x8d, 0x15, 0x68, 0x0a, 0xb3, 0x46, 0xe0, 0xbd, 0x59, 0xe9, 0xbd, 0xc7, + 0xe2, 0x2c, 0xb0, 0x39, 0x72, 0xdb, 0xd7, 0xaf, 0xfc, 0x23, 0x91, 0x67, 0x85, 0xbf, 0xb3, 0xe8, + 0x22, 0x32, 0x02, 0x15, 0x44, 0x29, 0xc7, 0x5f, 0x85, 0x45, 0xea, 0x18, 0x07, 0x5d, 0xba, 0xcb, + 0x3a, 0x1d, 0xdb, 0xe9, 0x94, 0xe7, 0xd6, 0xd0, 0xfa, 0x7c, 0xfd, 0x96, 0x62, 0xb6, 0xb8, 0x1d, + 0x9f, 0x24, 0x49, 0xd9, 0xf3, 0xba, 0xd5, 0xfc, 0x0b, 0x74, 0xab, 0x51, 0x7e, 0x14, 0x2e, 0xca, + 0x0f, 0xed, 0x0f, 0x59, 0xc0, 0x09, 0x6f, 0x8a, 0x12, 0xe8, 0x8b, 0x73, 0xfb, 0xa2, 0x13, 0x1f, + 0x56, 0x25, 0xff, 0x7a, 0x5a, 0x51, 0xb8, 0x17, 0xc9, 0xf9, 0x24, 0x03, 0xfc, 0x43, 0x28, 0x72, + 0xcf, 0x68, 0xb7, 0x6d, 0x53, 0x72, 0x54, 0xa9, 0xb3, 0x75, 0x65, 0x46, 0xf2, 0x27, 0x31, 0x3d, + 0x74, 0xd5, 0x7e, 0x4c, 0x57, 0x74, 0x5e, 0x8b, 0x8f, 0x92, 0x04, 0x1e, 0xfe, 0x19, 0x82, 0x92, + 0x38, 0x43, 0xc4, 0x45, 0xd4, 0xa5, 0xfa, 0x1b, 0xff, 0x2f, 0x09, 0x92, 0xd2, 0x17, 0xdd, 0xe9, + 0xd2, 0x33, 0x64, 0x0c, 0x5b, 0xfb, 0x17, 0x82, 0xd5, 0x31, 0xdf, 0xf5, 0x6f, 0xe2, 0x79, 0xef, + 0x4d, 0xc8, 0x8b, 0xf6, 0x37, 0x6a, 0x36, 0x4f, 0xa7, 0x18, 0x15, 0x51, 0x1b, 0x8e, 0xfa, 0xb6, + 0x18, 0xf3, 0x49, 0x00, 0xa9, 0xfd, 0x33, 0x07, 0xa5, 0x91, 0x90, 0xbf, 0xd7, 0xef, 0xf5, 0x0c, + 0xef, 0x26, 0x4e, 0xab, 0xbf, 0x46, 0xb0, 0x1c, 0x8f, 0x47, 0x3b, 0xb4, 0xbe, 0x35, 0x45, 0xeb, + 0x83, 0x20, 0xb8, 0xad, 0x98, 0x2c, 0x37, 0x93, 0x80, 0x24, 0xcd, 0x00, 0xff, 0x11, 0xc1, 0xdd, + 0x00, 0x45, 0xbd, 0xf3, 0xa6, 0x56, 0xa8, 0xf8, 0x9c, 0x3e, 0xc5, 0xcf, 0x2a, 0x8a, 0x77, 0x37, + 0x3f, 0x00, 0x9d, 0x7c, 0x20, 0x37, 0xfc, 0x36, 0x82, 0x5b, 0x81, 0x40, 0x9a, 0x75, 0xee, 0x9a, + 0x58, 0x7f, 0x5a, 0xb1, 0xbe, 0xb5, 0x79, 0x1e, 0x2c, 0x39, 0x9f, 0x8d, 0x66, 0x40, 0x31, 0xfe, + 0xc4, 0x71, 0x1d, 0x4f, 0x55, 0xbf, 0x47, 0x30, 0xa7, 0x1a, 0x1a, 0x7e, 0x10, 0xbb, 0xc5, 0x05, + 0x10, 0xe5, 0xcb, 0x6f, 0x70, 0xb8, 0xa9, 0xee, 0x8f, 0x99, 0x4b, 0xa2, 0xbf, 0xcf, 0xed, 0xae, + 0x1e, 0xfc, 0x82, 0xae, 0x37, 0x1c, 0xfe, 0xc4, 0xdb, 0xe3, 0x9e, 0xed, 0x74, 0xea, 0xf3, 0xa9, + 0xdb, 0xe6, 0xe7, 0x60, 0x8e, 0x3a, 0xf2, 0x6a, 0x2a, 0x8f, 0x0c, 0xf9, 0xfa, 0xc2, 0x70, 0x50, + 0x9d, 0xdb, 0x0e, 0x86, 0xc8, 0x68, 0x4e, 0xa3, 0x50, 0x52, 0xbc, 0xaf, 0x73, 0x7f, 0xea, 0xf7, + 0x9e, 0x9f, 0x56, 0x66, 0xde, 0x3d, 0xad, 0xcc, 0xbc, 0x77, 0x5a, 0x99, 0xf9, 0xd1, 0xb0, 0x82, + 0x9e, 0x0f, 0x2b, 0xe8, 0xdd, 0x61, 0x05, 0xbd, 0x37, 0xac, 0xa0, 0xbf, 0x0d, 0x2b, 0xe8, 0x17, + 0xef, 0x57, 0x66, 0xbe, 0x3d, 0xa7, 0x5c, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd0, 0x74, + 0xca, 0xeb, 0xe2, 0x21, 0x00, 0x00, } func (m *AddressGroup) Marshal() (dAtA []byte, err error) { @@ -1280,6 +1371,161 @@ func (m *ClusterGroupMembers) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EgressGroup) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EgressGroup) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EgressGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.GroupMembers) > 0 { + for iNdEx := len(m.GroupMembers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *EgressGroupList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EgressGroupList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EgressGroupList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Items) > 0 { + for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Items[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ListMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *EgressGroupPatch) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EgressGroupPatch) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EgressGroupPatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RemovedGroupMembers) > 0 { + for iNdEx := len(m.RemovedGroupMembers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.RemovedGroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.AddedGroupMembers) > 0 { + for iNdEx := len(m.AddedGroupMembers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AddedGroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + { + size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func (m *ExternalEntityReference) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -2358,29 +2604,33 @@ func (m *ClusterGroupMembers) Size() (n int) { return n } -func (m *ExternalEntityReference) Size() (n int) { +func (m *EgressGroup) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Name) - n += 1 + l + sovGenerated(uint64(l)) - l = len(m.Namespace) + l = m.ObjectMeta.Size() n += 1 + l + sovGenerated(uint64(l)) + if len(m.GroupMembers) > 0 { + for _, e := range m.GroupMembers { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } return n } -func (m *GroupAssociation) Size() (n int) { +func (m *EgressGroupList) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = m.ObjectMeta.Size() + l = m.ListMeta.Size() n += 1 + l + sovGenerated(uint64(l)) - if len(m.AssociatedGroups) > 0 { - for _, e := range m.AssociatedGroups { + if len(m.Items) > 0 { + for _, e := range m.Items { l = e.Size() n += 1 + l + sovGenerated(uint64(l)) } @@ -2388,17 +2638,70 @@ func (m *GroupAssociation) Size() (n int) { return n } -func (m *GroupMember) Size() (n int) { +func (m *EgressGroupPatch) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.Pod != nil { - l = m.Pod.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - if m.ExternalEntity != nil { + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.AddedGroupMembers) > 0 { + for _, e := range m.AddedGroupMembers { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + if len(m.RemovedGroupMembers) > 0 { + for _, e := range m.RemovedGroupMembers { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *ExternalEntityReference) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.Namespace) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *GroupAssociation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.ObjectMeta.Size() + n += 1 + l + sovGenerated(uint64(l)) + if len(m.AssociatedGroups) > 0 { + for _, e := range m.AssociatedGroups { + l = e.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + } + return n +} + +func (m *GroupMember) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Pod != nil { + l = m.Pod.Size() + n += 1 + l + sovGenerated(uint64(l)) + } + if m.ExternalEntity != nil { l = m.ExternalEntity.Size() n += 1 + l + sovGenerated(uint64(l)) } @@ -2853,6 +3156,60 @@ func (this *ClusterGroupMembers) String() string { }, "") return s } +func (this *EgressGroup) String() string { + if this == nil { + return "nil" + } + repeatedStringForGroupMembers := "[]GroupMember{" + for _, f := range this.GroupMembers { + repeatedStringForGroupMembers += strings.Replace(strings.Replace(f.String(), "GroupMember", "GroupMember", 1), `&`, ``, 1) + "," + } + repeatedStringForGroupMembers += "}" + s := strings.Join([]string{`&EgressGroup{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `GroupMembers:` + repeatedStringForGroupMembers + `,`, + `}`, + }, "") + return s +} +func (this *EgressGroupList) String() string { + if this == nil { + return "nil" + } + repeatedStringForItems := "[]EgressGroup{" + for _, f := range this.Items { + repeatedStringForItems += strings.Replace(strings.Replace(f.String(), "EgressGroup", "EgressGroup", 1), `&`, ``, 1) + "," + } + repeatedStringForItems += "}" + s := strings.Join([]string{`&EgressGroupList{`, + `ListMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ListMeta), "ListMeta", "v1.ListMeta", 1), `&`, ``, 1) + `,`, + `Items:` + repeatedStringForItems + `,`, + `}`, + }, "") + return s +} +func (this *EgressGroupPatch) String() string { + if this == nil { + return "nil" + } + repeatedStringForAddedGroupMembers := "[]GroupMember{" + for _, f := range this.AddedGroupMembers { + repeatedStringForAddedGroupMembers += strings.Replace(strings.Replace(f.String(), "GroupMember", "GroupMember", 1), `&`, ``, 1) + "," + } + repeatedStringForAddedGroupMembers += "}" + repeatedStringForRemovedGroupMembers := "[]GroupMember{" + for _, f := range this.RemovedGroupMembers { + repeatedStringForRemovedGroupMembers += strings.Replace(strings.Replace(f.String(), "GroupMember", "GroupMember", 1), `&`, ``, 1) + "," + } + repeatedStringForRemovedGroupMembers += "}" + s := strings.Join([]string{`&EgressGroupPatch{`, + `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, + `AddedGroupMembers:` + repeatedStringForAddedGroupMembers + `,`, + `RemovedGroupMembers:` + repeatedStringForRemovedGroupMembers + `,`, + `}`, + }, "") + return s +} func (this *ExternalEntityReference) String() string { if this == nil { return "nil" @@ -4059,6 +4416,400 @@ func (m *ClusterGroupMembers) Unmarshal(dAtA []byte) error { } return nil } +func (m *EgressGroup) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressGroup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressGroup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupMembers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GroupMembers = append(m.GroupMembers, GroupMember{}) + if err := m.GroupMembers[len(m.GroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressGroupList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressGroupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressGroupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ListMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ListMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Items = append(m.Items, EgressGroup{}) + if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EgressGroupPatch) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EgressGroupPatch: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EgressGroupPatch: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AddedGroupMembers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AddedGroupMembers = append(m.AddedGroupMembers, GroupMember{}) + if err := m.AddedGroupMembers[len(m.AddedGroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RemovedGroupMembers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RemovedGroupMembers = append(m.RemovedGroupMembers, GroupMember{}) + if err := m.RemovedGroupMembers[len(m.RemovedGroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ExternalEntityReference) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/controlplane/v1beta2/generated.proto b/pkg/apis/controlplane/v1beta2/generated.proto index c221eae4a2a..0f4d54be45c 100644 --- a/pkg/apis/controlplane/v1beta2/generated.proto +++ b/pkg/apis/controlplane/v1beta2/generated.proto @@ -94,6 +94,35 @@ message ClusterGroupMembers { repeated GroupMember effectiveMembers = 2; } +// +genclient +// +genclient:nonNamespaced +// +genclient:onlyVerbs=list,get,watch +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +message EgressGroup { + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; + + // GroupMembers is list of resources selected by this group. + repeated GroupMember groupMembers = 2; +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupList is a list of EgressGroup objects. +message EgressGroupList { + optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + + repeated EgressGroup items = 2; +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupPatch describes the incremental update of an EgressGroup. +message EgressGroupPatch { + optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta objectMeta = 1; + + repeated GroupMember addedGroupMembers = 2; + + repeated GroupMember removedGroupMembers = 3; +} + // ExternalEntityReference represents a ExternalEntity Reference. message ExternalEntityReference { // The name of this ExternalEntity. diff --git a/pkg/apis/controlplane/v1beta2/register.go b/pkg/apis/controlplane/v1beta2/register.go index e3e05056477..b24a1422505 100644 --- a/pkg/apis/controlplane/v1beta2/register.go +++ b/pkg/apis/controlplane/v1beta2/register.go @@ -72,6 +72,9 @@ func addKnownTypes(scheme *runtime.Scheme) error { &NodeStatsSummary{}, &ClusterGroupMembers{}, &GroupAssociation{}, + &EgressGroup{}, + &EgressGroupPatch{}, + &EgressGroupList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/apis/controlplane/v1beta2/types.go b/pkg/apis/controlplane/v1beta2/types.go index e8853bedbc4..df4200efd32 100644 --- a/pkg/apis/controlplane/v1beta2/types.go +++ b/pkg/apis/controlplane/v1beta2/types.go @@ -341,3 +341,31 @@ type GroupAssociation struct { // Pod/ExternalEntity being queried. AssociatedGroups []GroupReference `json:"associatedGroups" protobuf:"bytes,2,rep,name=associatedGroups"` } + +// +genclient +// +genclient:nonNamespaced +// +genclient:onlyVerbs=list,get,watch +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type EgressGroup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + // GroupMembers is list of resources selected by this group. + GroupMembers []GroupMember `json:"groupMembers,omitempty" protobuf:"bytes,2,rep,name=groupMembers"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupPatch describes the incremental update of an EgressGroup. +type EgressGroupPatch struct { + metav1.TypeMeta + metav1.ObjectMeta `protobuf:"bytes,1,opt,name=objectMeta"` + AddedGroupMembers []GroupMember `protobuf:"bytes,2,rep,name=addedGroupMembers"` + RemovedGroupMembers []GroupMember `protobuf:"bytes,3,rep,name=removedGroupMembers"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// EgressGroupList is a list of EgressGroup objects. +type EgressGroupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + Items []EgressGroup `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go index 40c2027efae..771a63fbe79 100644 --- a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go +++ b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go @@ -107,6 +107,36 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*EgressGroup)(nil), (*controlplane.EgressGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_EgressGroup_To_controlplane_EgressGroup(a.(*EgressGroup), b.(*controlplane.EgressGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*controlplane.EgressGroup)(nil), (*EgressGroup)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_controlplane_EgressGroup_To_v1beta2_EgressGroup(a.(*controlplane.EgressGroup), b.(*EgressGroup), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EgressGroupList)(nil), (*controlplane.EgressGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_EgressGroupList_To_controlplane_EgressGroupList(a.(*EgressGroupList), b.(*controlplane.EgressGroupList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*controlplane.EgressGroupList)(nil), (*EgressGroupList)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_controlplane_EgressGroupList_To_v1beta2_EgressGroupList(a.(*controlplane.EgressGroupList), b.(*EgressGroupList), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*EgressGroupPatch)(nil), (*controlplane.EgressGroupPatch)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_EgressGroupPatch_To_controlplane_EgressGroupPatch(a.(*EgressGroupPatch), b.(*controlplane.EgressGroupPatch), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*controlplane.EgressGroupPatch)(nil), (*EgressGroupPatch)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(a.(*controlplane.EgressGroupPatch), b.(*EgressGroupPatch), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*ExternalEntityReference)(nil), (*controlplane.ExternalEntityReference)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta2_ExternalEntityReference_To_controlplane_ExternalEntityReference(a.(*ExternalEntityReference), b.(*controlplane.ExternalEntityReference), scope) }); err != nil { @@ -458,6 +488,74 @@ func Convert_controlplane_ClusterGroupMembers_To_v1beta2_ClusterGroupMembers(in return autoConvert_controlplane_ClusterGroupMembers_To_v1beta2_ClusterGroupMembers(in, out, s) } +func autoConvert_v1beta2_EgressGroup_To_controlplane_EgressGroup(in *EgressGroup, out *controlplane.EgressGroup, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.GroupMembers = *(*[]controlplane.GroupMember)(unsafe.Pointer(&in.GroupMembers)) + return nil +} + +// Convert_v1beta2_EgressGroup_To_controlplane_EgressGroup is an autogenerated conversion function. +func Convert_v1beta2_EgressGroup_To_controlplane_EgressGroup(in *EgressGroup, out *controlplane.EgressGroup, s conversion.Scope) error { + return autoConvert_v1beta2_EgressGroup_To_controlplane_EgressGroup(in, out, s) +} + +func autoConvert_controlplane_EgressGroup_To_v1beta2_EgressGroup(in *controlplane.EgressGroup, out *EgressGroup, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.GroupMembers = *(*[]GroupMember)(unsafe.Pointer(&in.GroupMembers)) + return nil +} + +// Convert_controlplane_EgressGroup_To_v1beta2_EgressGroup is an autogenerated conversion function. +func Convert_controlplane_EgressGroup_To_v1beta2_EgressGroup(in *controlplane.EgressGroup, out *EgressGroup, s conversion.Scope) error { + return autoConvert_controlplane_EgressGroup_To_v1beta2_EgressGroup(in, out, s) +} + +func autoConvert_v1beta2_EgressGroupList_To_controlplane_EgressGroupList(in *EgressGroupList, out *controlplane.EgressGroupList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]controlplane.EgressGroup)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_v1beta2_EgressGroupList_To_controlplane_EgressGroupList is an autogenerated conversion function. +func Convert_v1beta2_EgressGroupList_To_controlplane_EgressGroupList(in *EgressGroupList, out *controlplane.EgressGroupList, s conversion.Scope) error { + return autoConvert_v1beta2_EgressGroupList_To_controlplane_EgressGroupList(in, out, s) +} + +func autoConvert_controlplane_EgressGroupList_To_v1beta2_EgressGroupList(in *controlplane.EgressGroupList, out *EgressGroupList, s conversion.Scope) error { + out.ListMeta = in.ListMeta + out.Items = *(*[]EgressGroup)(unsafe.Pointer(&in.Items)) + return nil +} + +// Convert_controlplane_EgressGroupList_To_v1beta2_EgressGroupList is an autogenerated conversion function. +func Convert_controlplane_EgressGroupList_To_v1beta2_EgressGroupList(in *controlplane.EgressGroupList, out *EgressGroupList, s conversion.Scope) error { + return autoConvert_controlplane_EgressGroupList_To_v1beta2_EgressGroupList(in, out, s) +} + +func autoConvert_v1beta2_EgressGroupPatch_To_controlplane_EgressGroupPatch(in *EgressGroupPatch, out *controlplane.EgressGroupPatch, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.AddedGroupMembers = *(*[]controlplane.GroupMember)(unsafe.Pointer(&in.AddedGroupMembers)) + out.RemovedGroupMembers = *(*[]controlplane.GroupMember)(unsafe.Pointer(&in.RemovedGroupMembers)) + return nil +} + +// Convert_v1beta2_EgressGroupPatch_To_controlplane_EgressGroupPatch is an autogenerated conversion function. +func Convert_v1beta2_EgressGroupPatch_To_controlplane_EgressGroupPatch(in *EgressGroupPatch, out *controlplane.EgressGroupPatch, s conversion.Scope) error { + return autoConvert_v1beta2_EgressGroupPatch_To_controlplane_EgressGroupPatch(in, out, s) +} + +func autoConvert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(in *controlplane.EgressGroupPatch, out *EgressGroupPatch, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.AddedGroupMembers = *(*[]GroupMember)(unsafe.Pointer(&in.AddedGroupMembers)) + out.RemovedGroupMembers = *(*[]GroupMember)(unsafe.Pointer(&in.RemovedGroupMembers)) + return nil +} + +// Convert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch is an autogenerated conversion function. +func Convert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(in *controlplane.EgressGroupPatch, out *EgressGroupPatch, s conversion.Scope) error { + return autoConvert_controlplane_EgressGroupPatch_To_v1beta2_EgressGroupPatch(in, out, s) +} + func autoConvert_v1beta2_ExternalEntityReference_To_controlplane_ExternalEntityReference(in *ExternalEntityReference, out *controlplane.ExternalEntityReference, s conversion.Scope) error { out.Name = in.Name out.Namespace = in.Namespace diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go index 55c33fa5ce0..8d5f8fdc249 100644 --- a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go @@ -270,6 +270,112 @@ func (in *ClusterGroupMembers) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroup) DeepCopyInto(out *EgressGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.GroupMembers != nil { + in, out := &in.GroupMembers, &out.GroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroup. +func (in *EgressGroup) DeepCopy() *EgressGroup { + if in == nil { + return nil + } + out := new(EgressGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroupList) DeepCopyInto(out *EgressGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EgressGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroupList. +func (in *EgressGroupList) DeepCopy() *EgressGroupList { + if in == nil { + return nil + } + out := new(EgressGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroupPatch) DeepCopyInto(out *EgressGroupPatch) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.AddedGroupMembers != nil { + in, out := &in.AddedGroupMembers, &out.AddedGroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RemovedGroupMembers != nil { + in, out := &in.RemovedGroupMembers, &out.RemovedGroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroupPatch. +func (in *EgressGroupPatch) DeepCopy() *EgressGroupPatch { + if in == nil { + return nil + } + out := new(EgressGroupPatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroupPatch) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalEntityReference) DeepCopyInto(out *ExternalEntityReference) { *out = *in diff --git a/pkg/apis/controlplane/zz_generated.deepcopy.go b/pkg/apis/controlplane/zz_generated.deepcopy.go index 67983707d91..b9278c01a5d 100644 --- a/pkg/apis/controlplane/zz_generated.deepcopy.go +++ b/pkg/apis/controlplane/zz_generated.deepcopy.go @@ -270,6 +270,112 @@ func (in *ClusterGroupMembers) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroup) DeepCopyInto(out *EgressGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.GroupMembers != nil { + in, out := &in.GroupMembers, &out.GroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroup. +func (in *EgressGroup) DeepCopy() *EgressGroup { + if in == nil { + return nil + } + out := new(EgressGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroupList) DeepCopyInto(out *EgressGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]EgressGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroupList. +func (in *EgressGroupList) DeepCopy() *EgressGroupList { + if in == nil { + return nil + } + out := new(EgressGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EgressGroupPatch) DeepCopyInto(out *EgressGroupPatch) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.AddedGroupMembers != nil { + in, out := &in.AddedGroupMembers, &out.AddedGroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RemovedGroupMembers != nil { + in, out := &in.RemovedGroupMembers, &out.RemovedGroupMembers + *out = make([]GroupMember, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressGroupPatch. +func (in *EgressGroupPatch) DeepCopy() *EgressGroupPatch { + if in == nil { + return nil + } + out := new(EgressGroupPatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *EgressGroupPatch) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalEntityReference) DeepCopyInto(out *ExternalEntityReference) { *out = *in diff --git a/pkg/apis/crd/v1alpha2/register.go b/pkg/apis/crd/v1alpha2/register.go index f1b878cfb84..5535b6785dc 100644 --- a/pkg/apis/crd/v1alpha2/register.go +++ b/pkg/apis/crd/v1alpha2/register.go @@ -48,6 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &ExternalEntityList{}, &ClusterGroup{}, &ClusterGroupList{}, + &Egress{}, + &EgressList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 58c2389e68b..e1eb886430b 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -38,6 +38,7 @@ import ( "github.com/vmware-tanzu/antrea/pkg/apiserver/handlers/endpoint" "github.com/vmware-tanzu/antrea/pkg/apiserver/handlers/loglevel" "github.com/vmware-tanzu/antrea/pkg/apiserver/handlers/webhook" + "github.com/vmware-tanzu/antrea/pkg/apiserver/registry/controlplane/egressgroup" "github.com/vmware-tanzu/antrea/pkg/apiserver/registry/controlplane/nodestatssummary" "github.com/vmware-tanzu/antrea/pkg/apiserver/registry/networkpolicy/addressgroup" "github.com/vmware-tanzu/antrea/pkg/apiserver/registry/networkpolicy/appliedtogroup" @@ -93,6 +94,7 @@ type ExtraConfig struct { addressGroupStore storage.Interface appliedToGroupStore storage.Interface networkPolicyStore storage.Interface + egressGroupStore storage.Interface controllerQuerier querier.ControllerQuerier endpointQuerier controllernetworkpolicy.EndpointQuerier networkPolicyController *controllernetworkpolicy.NetworkPolicyController @@ -130,7 +132,7 @@ type completedConfig struct { func NewConfig( genericConfig *genericapiserver.Config, - addressGroupStore, appliedToGroupStore, networkPolicyStore, groupStore storage.Interface, + addressGroupStore, appliedToGroupStore, networkPolicyStore, groupStore, egressGroupStore storage.Interface, caCertController *certificate.CACertController, statsAggregator *stats.Aggregator, controllerQuerier querier.ControllerQuerier, @@ -143,6 +145,7 @@ func NewConfig( addressGroupStore: addressGroupStore, appliedToGroupStore: appliedToGroupStore, networkPolicyStore: networkPolicyStore, + egressGroupStore: egressGroupStore, caCertController: caCertController, statsAggregator: statsAggregator, controllerQuerier: controllerQuerier, @@ -165,6 +168,7 @@ func installAPIGroup(s *APIServer, c completedConfig) error { clusterGroupMembershipStorage := clustergroupmember.NewREST(c.extraConfig.networkPolicyController) groupAssociationStorage := groupassociation.NewREST(c.extraConfig.networkPolicyController) nodeStatsSummaryStorage := nodestatssummary.NewREST(c.extraConfig.statsAggregator) + egressGroupStorage := egressgroup.NewREST(c.extraConfig.egressGroupStore) cpGroup := genericapiserver.NewDefaultAPIGroupInfo(controlplane.GroupName, Scheme, metav1.ParameterCodec, Codecs) cpv1beta2Storage := map[string]rest.Storage{} cpv1beta2Storage["addressgroups"] = addressGroupStorage @@ -200,6 +204,7 @@ func installAPIGroup(s *APIServer, c completedConfig) error { legacyCPv1beta1Storage["appliedtogroups"] = appliedToGroupStorage legacyCPv1beta1Storage["networkpolicies"] = networkPolicyStorage legacyCPv1beta1Storage["nodestatssummaries"] = nodeStatsSummaryStorage + legacyCPv1beta1Storage["egressgroups"] = egressGroupStorage legacyCPGroup.VersionedResourcesStorageMap["v1beta1"] = legacyCPv1beta1Storage legacyCPGroup.VersionedResourcesStorageMap["v1beta2"] = cpv1beta2Storage diff --git a/pkg/apiserver/openapi/zz_generated.openapi.go b/pkg/apiserver/openapi/zz_generated.openapi.go index 2154843eb59..a734d150361 100644 --- a/pkg/apiserver/openapi/zz_generated.openapi.go +++ b/pkg/apiserver/openapi/zz_generated.openapi.go @@ -58,6 +58,9 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.AppliedToGroupList": schema_pkg_apis_controlplane_v1beta2_AppliedToGroupList(ref), "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.AppliedToGroupPatch": schema_pkg_apis_controlplane_v1beta2_AppliedToGroupPatch(ref), "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.ClusterGroupMembers": schema_pkg_apis_controlplane_v1beta2_ClusterGroupMembers(ref), + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.EgressGroup": schema_pkg_apis_controlplane_v1beta2_EgressGroup(ref), + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.EgressGroupList": schema_pkg_apis_controlplane_v1beta2_EgressGroupList(ref), + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.EgressGroupPatch": schema_pkg_apis_controlplane_v1beta2_EgressGroupPatch(ref), "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.ExternalEntityReference": schema_pkg_apis_controlplane_v1beta2_ExternalEntityReference(ref), "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupAssociation": schema_pkg_apis_controlplane_v1beta2_GroupAssociation(ref), "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember": schema_pkg_apis_controlplane_v1beta2_GroupMember(ref), @@ -1761,6 +1764,149 @@ func schema_pkg_apis_controlplane_v1beta2_ClusterGroupMembers(ref common.Referen } } +func schema_pkg_apis_controlplane_v1beta2_EgressGroup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "groupMembers": { + SchemaProps: spec.SchemaProps{ + Description: "GroupMembers is list of resources selected by this group.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_controlplane_v1beta2_EgressGroupList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EgressGroupList is a list of EgressGroup objects.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.EgressGroup"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.EgressGroup", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_controlplane_v1beta2_EgressGroupPatch(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "EgressGroupPatch describes the incremental update of an EgressGroup.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "TypeMeta": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta"), + }, + }, + "ObjectMeta": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "AddedGroupMembers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember"), + }, + }, + }, + }, + }, + "RemovedGroupMembers": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember"), + }, + }, + }, + }, + }, + }, + Required: []string{"TypeMeta", "ObjectMeta", "AddedGroupMembers", "RemovedGroupMembers"}, + }, + }, + Dependencies: []string{ + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2.GroupMember", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta", "k8s.io/apimachinery/pkg/apis/meta/v1.TypeMeta"}, + } +} + func schema_pkg_apis_controlplane_v1beta2_ExternalEntityReference(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apiserver/registry/controlplane/egressgroup/rest.go b/pkg/apiserver/registry/controlplane/egressgroup/rest.go new file mode 100644 index 00000000000..b41d6b49f28 --- /dev/null +++ b/pkg/apiserver/registry/controlplane/egressgroup/rest.go @@ -0,0 +1,103 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package egressgroup + +import ( + "context" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/registry/rest" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" + "github.com/vmware-tanzu/antrea/pkg/apiserver/registry/networkpolicy" + "github.com/vmware-tanzu/antrea/pkg/apiserver/storage" + "github.com/vmware-tanzu/antrea/pkg/controller/egress/store" + "github.com/vmware-tanzu/antrea/pkg/controller/types" +) + +// REST implements rest.Storage for EgressGroups. +type REST struct { + egressGroupStore storage.Interface +} + +var ( + _ rest.Storage = &REST{} + _ rest.Watcher = &REST{} + _ rest.Scoper = &REST{} + _ rest.Lister = &REST{} + _ rest.Getter = &REST{} +) + +// NewREST returns a REST object that will work against API services. +func NewREST(egressGroupStore storage.Interface) *REST { + return &REST{egressGroupStore} +} + +func (r *REST) New() runtime.Object { + return &controlplane.EgressGroup{} +} + +func (r *REST) NewList() runtime.Object { + return &controlplane.EgressGroupList{} +} + +func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { + egressGroup, exists, err := r.egressGroupStore.Get(name) + if err != nil { + return nil, errors.NewInternalError(err) + } + if !exists { + return nil, errors.NewNotFound(controlplane.Resource("egressgroup"), name) + } + obj := new(controlplane.EgressGroup) + store.ToEgressGroupMsg(egressGroup.(*types.EgressGroup), obj, true, nil) + return obj, nil +} + +func (r *REST) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { + labelSelector := labels.Everything() + if options != nil && options.LabelSelector != nil { + labelSelector = options.LabelSelector + } + egressGroups := r.egressGroupStore.List() + items := make([]controlplane.EgressGroup, 0, len(egressGroups)) + for i := range egressGroups { + var item controlplane.EgressGroup + store.ToEgressGroupMsg(egressGroups[i].(*types.EgressGroup), &item, true, nil) + if labelSelector.Matches(labels.Set(item.Labels)) { + items = append(items, item) + } + } + list := &controlplane.EgressGroupList{Items: items} + return list, nil +} + +func (r *REST) NamespaceScoped() bool { + return false +} + +func (r *REST) Watch(ctx context.Context, options *internalversion.ListOptions) (watch.Interface, error) { + key, label, field := networkpolicy.GetSelectors(options) + return r.egressGroupStore.Watch(ctx, key, label, field) +} + +func (r *REST) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { + return rest.NewDefaultTableConvertor(controlplane.Resource("egressgroup")).ConvertToTable(ctx, obj, tableOptions) +} diff --git a/pkg/apiserver/registry/controlplane/egressgroup/rest_test.go b/pkg/apiserver/registry/controlplane/egressgroup/rest_test.go new file mode 100644 index 00000000000..08850e570a0 --- /dev/null +++ b/pkg/apiserver/registry/controlplane/egressgroup/rest_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package egressgroup + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/internalversion" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" + "github.com/vmware-tanzu/antrea/pkg/controller/egress/store" + "github.com/vmware-tanzu/antrea/pkg/controller/types" +) + +func TestRESTList(t *testing.T) { + tests := []struct { + name string + egressGroups []*types.EgressGroup + labelSelector labels.Selector + expectedObj runtime.Object + }{ + { + name: "label selector selecting nothing", + egressGroups: []*types.EgressGroup{ + { + Name: "foo", + }, + }, + labelSelector: labels.Nothing(), + expectedObj: &controlplane.EgressGroupList{}, + }, + { + name: "label selector selecting everything", + egressGroups: []*types.EgressGroup{ + { + Name: "foo", + }, + }, + labelSelector: labels.Everything(), + expectedObj: &controlplane.EgressGroupList{ + Items: []controlplane.EgressGroup{ + { + ObjectMeta: v1.ObjectMeta{ + Name: "foo", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + storage := store.NewEgressGroupStore() + for _, obj := range tt.egressGroups { + storage.Create(obj) + } + r := NewREST(storage) + actualObj, err := r.List(context.TODO(), &internalversion.ListOptions{LabelSelector: tt.labelSelector}) + assert.NoError(t, err) + assert.ElementsMatch(t, tt.expectedObj.(*controlplane.EgressGroupList).Items, actualObj.(*controlplane.EgressGroupList).Items) + }) + } +} diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go index 69181ef1291..6cdc107de88 100644 --- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go +++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/controlplane_client.go @@ -26,6 +26,7 @@ type ControlplaneV1beta2Interface interface { RESTClient() rest.Interface AddressGroupsGetter AppliedToGroupsGetter + EgressGroupsGetter NetworkPoliciesGetter NodeStatsSummariesGetter } @@ -43,6 +44,10 @@ func (c *ControlplaneV1beta2Client) AppliedToGroups() AppliedToGroupInterface { return newAppliedToGroups(c) } +func (c *ControlplaneV1beta2Client) EgressGroups() EgressGroupInterface { + return newEgressGroups(c) +} + func (c *ControlplaneV1beta2Client) NetworkPolicies() NetworkPolicyInterface { return newNetworkPolicies(c) } diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/egressgroup.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/egressgroup.go new file mode 100644 index 00000000000..986352c9339 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/egressgroup.go @@ -0,0 +1,96 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by client-gen. DO NOT EDIT. + +package v1beta2 + +import ( + "context" + "time" + + v1beta2 "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2" + scheme "github.com/vmware-tanzu/antrea/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// EgressGroupsGetter has a method to return a EgressGroupInterface. +// A group's client should implement this interface. +type EgressGroupsGetter interface { + EgressGroups() EgressGroupInterface +} + +// EgressGroupInterface has methods to work with EgressGroup resources. +type EgressGroupInterface interface { + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1beta2.EgressGroup, error) + List(ctx context.Context, opts v1.ListOptions) (*v1beta2.EgressGroupList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + EgressGroupExpansion +} + +// egressGroups implements EgressGroupInterface +type egressGroups struct { + client rest.Interface +} + +// newEgressGroups returns a EgressGroups +func newEgressGroups(c *ControlplaneV1beta2Client) *egressGroups { + return &egressGroups{ + client: c.RESTClient(), + } +} + +// Get takes name of the egressGroup, and returns the corresponding egressGroup object, and an error if there is any. +func (c *egressGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.EgressGroup, err error) { + result = &v1beta2.EgressGroup{} + err = c.client.Get(). + Resource("egressgroups"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of EgressGroups that match those selectors. +func (c *egressGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.EgressGroupList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1beta2.EgressGroupList{} + err = c.client.Get(). + Resource("egressgroups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested egressGroups. +func (c *egressGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("egressgroups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go index 984d2b6ead3..fdf53986686 100644 --- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go +++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_controlplane_client.go @@ -34,6 +34,10 @@ func (c *FakeControlplaneV1beta2) AppliedToGroups() v1beta2.AppliedToGroupInterf return &FakeAppliedToGroups{c} } +func (c *FakeControlplaneV1beta2) EgressGroups() v1beta2.EgressGroupInterface { + return &FakeEgressGroups{c} +} + func (c *FakeControlplaneV1beta2) NetworkPolicies() v1beta2.NetworkPolicyInterface { return &FakeNetworkPolicies{c} } diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_egressgroup.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_egressgroup.go new file mode 100644 index 00000000000..233ad2a2581 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/fake/fake_egressgroup.go @@ -0,0 +1,74 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1beta2 "github.com/vmware-tanzu/antrea/pkg/apis/controlplane/v1beta2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeEgressGroups implements EgressGroupInterface +type FakeEgressGroups struct { + Fake *FakeControlplaneV1beta2 +} + +var egressgroupsResource = schema.GroupVersionResource{Group: "controlplane.antrea.io", Version: "v1beta2", Resource: "egressgroups"} + +var egressgroupsKind = schema.GroupVersionKind{Group: "controlplane.antrea.io", Version: "v1beta2", Kind: "EgressGroup"} + +// Get takes name of the egressGroup, and returns the corresponding egressGroup object, and an error if there is any. +func (c *FakeEgressGroups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1beta2.EgressGroup, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(egressgroupsResource, name), &v1beta2.EgressGroup{}) + if obj == nil { + return nil, err + } + return obj.(*v1beta2.EgressGroup), err +} + +// List takes label and field selectors, and returns the list of EgressGroups that match those selectors. +func (c *FakeEgressGroups) List(ctx context.Context, opts v1.ListOptions) (result *v1beta2.EgressGroupList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(egressgroupsResource, egressgroupsKind, opts), &v1beta2.EgressGroupList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1beta2.EgressGroupList{ListMeta: obj.(*v1beta2.EgressGroupList).ListMeta} + for _, item := range obj.(*v1beta2.EgressGroupList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested egressGroups. +func (c *FakeEgressGroups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(egressgroupsResource, opts)) +} diff --git a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go index b6a95dd3c11..ede2aab745c 100644 --- a/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/controlplane/v1beta2/generated_expansion.go @@ -20,4 +20,6 @@ type AddressGroupExpansion interface{} type AppliedToGroupExpansion interface{} +type EgressGroupExpansion interface{} + type NodeStatsSummaryExpansion interface{} diff --git a/pkg/controller/egress/controller.go b/pkg/controller/egress/controller.go new file mode 100644 index 00000000000..7525e3be48c --- /dev/null +++ b/pkg/controller/egress/controller.go @@ -0,0 +1,231 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package egress + +import ( + "reflect" + "time" + + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" + egressv1alpha2 "github.com/vmware-tanzu/antrea/pkg/apis/crd/v1alpha2" + "github.com/vmware-tanzu/antrea/pkg/apiserver/storage" + egressinformers "github.com/vmware-tanzu/antrea/pkg/client/informers/externalversions/crd/v1alpha2" + "github.com/vmware-tanzu/antrea/pkg/controller/grouping" + antreatypes "github.com/vmware-tanzu/antrea/pkg/controller/types" +) + +const ( + controllerName = "EgressController" + // Set resyncPeriod to 0 to disable resyncing. + resyncPeriod time.Duration = 0 + // How long to wait before retrying the processing of an Egress change. + minRetryDelay = 5 * time.Second + maxRetryDelay = 300 * time.Second + // Default number of workers processing an Egress change. + defaultWorkers = 4 + // egressGroupType is the type used when registering EgressGroups to the grouping interface. + egressGroupType grouping.GroupType = "egressGroup" +) + +// EgressController is responsible for synchronizing the EgressGroups selected by Egresses. +type EgressController struct { + egressInformer egressinformers.EgressInformer + // egressListerSynced is a function which returns true if the Egresses shared informer has been synced at least once. + egressListerSynced cache.InformerSynced + // egressGroupStore is the storage where the EgressGroups are stored. + egressGroupStore storage.Interface + // queue maintains the EgressGroup objects that need to be synced. + queue workqueue.RateLimitingInterface + // groupingInterface knows Pods that a given group selects. + groupingInterface grouping.Interface + // Added as a member to the struct to allow injection for testing. + groupingInterfaceSynced func() bool +} + +// NewEgressController returns a new *EgressController. +func NewEgressController(groupingInterface grouping.Interface, + egressInformer egressinformers.EgressInformer, + egressGroupStore storage.Interface) *EgressController { + c := &EgressController{ + egressInformer: egressInformer, + egressListerSynced: egressInformer.Informer().HasSynced, + egressGroupStore: egressGroupStore, + queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "egress"), + groupingInterface: groupingInterface, + groupingInterfaceSynced: groupingInterface.HasSynced, + } + // Add handlers for Group events and Egress events. + c.groupingInterface.AddEventHandler(egressGroupType, c.enqueueEgressGroup) + egressInformer.Informer().AddEventHandlerWithResyncPeriod( + cache.ResourceEventHandlerFuncs{ + AddFunc: c.addEgress, + UpdateFunc: c.updateEgress, + DeleteFunc: c.deleteEgress, + }, + resyncPeriod, + ) + return c +} + +// Run begins watching and syncing of the EgressController. +func (c *EgressController) Run(stopCh <-chan struct{}) { + defer c.queue.ShutDown() + + klog.Infof("Starting %s", controllerName) + defer klog.Infof("Shutting down %s", controllerName) + + cacheSyncs := []cache.InformerSynced{c.egressListerSynced, c.groupingInterfaceSynced} + if !cache.WaitForNamedCacheSync(controllerName, stopCh, cacheSyncs...) { + return + } + + for i := 0; i < defaultWorkers; i++ { + go wait.Until(c.egressGroupWorker, time.Second, stopCh) + } + <-stopCh +} + +func (c *EgressController) egressGroupWorker() { + for c.processNextEgressGroupWorkItem() { + } +} + +func (c *EgressController) processNextEgressGroupWorkItem() bool { + key, quit := c.queue.Get() + if quit { + return false + } + defer c.queue.Done(key) + + err := c.syncEgressGroup(key.(string)) + if err != nil { + // Put the item back on the workqueue to handle any transient errors. + c.queue.AddRateLimited(key) + klog.Errorf("Failed to sync EgressGroup %s: %v", key, err) + return true + } + // If no error occurs we Forget this item so it does not get queued again until + // another change happens. + c.queue.Forget(key) + return true +} + +func (c *EgressController) syncEgressGroup(key string) error { + startTime := time.Now() + defer func() { + d := time.Since(startTime) + klog.V(2).Infof("Finished syncing EgressGroup %s. (%v)", key, d) + }() + + egressGroupObj, found, _ := c.egressGroupStore.Get(key) + if !found { + klog.V(2).Infof("EgressGroup %s not found", key) + return nil + } + + nodeNames := sets.String{} + podNum := 0 + memberSetByNode := make(map[string]controlplane.GroupMemberSet) + egressGroup := egressGroupObj.(*antreatypes.EgressGroup) + pods, _ := c.groupingInterface.GetEntities(egressGroupType, key) + for _, pod := range pods { + // Ignore Pod if it's not scheduled or not running. + // TODO: If a Pod is scheduled but not running, it can be included in the EgressGroup so that the agent can + // install its SNAT rule right after the Pod's CNI request is processed, which just requires a notification from + // CNIServer to the agent's EgressController. However the current notification mechanism (the entityUpdate + // channel) allows only single consumer. Once it allows multiple consumers, we can change the condition to + // include scheduled Pods that have no IPs. + if pod.Spec.NodeName == "" || len(pod.Status.PodIPs) == 0 { + continue + } + podNum++ + podSet := memberSetByNode[pod.Spec.NodeName] + if podSet == nil { + podSet = controlplane.GroupMemberSet{} + memberSetByNode[pod.Spec.NodeName] = podSet + } + groupMember := &controlplane.GroupMember{ + Pod: &controlplane.PodReference{ + Name: pod.Name, + Namespace: pod.Namespace, + }, + } + podSet.Insert(groupMember) + // Update the NodeNames in order to set the SpanMeta for EgressGroup. + nodeNames.Insert(pod.Spec.NodeName) + } + updatedEgressGroup := &antreatypes.EgressGroup{ + UID: egressGroup.UID, + Name: egressGroup.Name, + GroupMemberByNode: memberSetByNode, + SpanMeta: antreatypes.SpanMeta{NodeNames: nodeNames}, + } + klog.V(2).Infof("Updating existing EgressGroup %s with %d Pods on %d Nodes", key, podNum, nodeNames.Len()) + c.egressGroupStore.Update(updatedEgressGroup) + return nil +} + +func (c *EgressController) enqueueEgressGroup(key string) { + klog.V(4).Infof("Adding new key %s to EgressGroup queue", key) + c.queue.Add(key) +} + +// addEgress processes Egress ADD events and creates corresponding EgressGroup. +func (c *EgressController) addEgress(obj interface{}) { + egress := obj.(*egressv1alpha2.Egress) + klog.Infof("Processing Egress %s ADD event with selector (%s)", egress.Name, egress.Spec.AppliedTo) + // Create an EgressGroup object corresponding to this Egress and enqueue task to the workqueue. + egressGroup := &antreatypes.EgressGroup{ + Name: egress.Name, + UID: egress.UID, + } + c.egressGroupStore.Create(egressGroup) + // Register the group to the grouping interface. + groupSelector := antreatypes.NewGroupSelector("", egress.Spec.AppliedTo.PodSelector, egress.Spec.AppliedTo.NamespaceSelector, nil) + c.groupingInterface.AddGroup(egressGroupType, egress.Name, groupSelector) + c.queue.Add(egress.Name) +} + +// updateEgress processes Egress UPDATE events and updates corresponding EgressGroup. +func (c *EgressController) updateEgress(old, cur interface{}) { + oldEgress := old.(*egressv1alpha2.Egress) + curEgress := cur.(*egressv1alpha2.Egress) + klog.Infof("Processing Egress %s UPDATE event with selector (%s)", curEgress.Name, curEgress.Spec.AppliedTo) + // Do nothing if AppliedTo doesn't change. + // TODO: Define custom Equal function to be more efficient. + if reflect.DeepEqual(oldEgress.Spec.AppliedTo, curEgress.Spec.AppliedTo) { + return + } + + // Update the group's selector in the grouping interface. + groupSelector := antreatypes.NewGroupSelector("", curEgress.Spec.AppliedTo.PodSelector, curEgress.Spec.AppliedTo.NamespaceSelector, nil) + c.groupingInterface.AddGroup(egressGroupType, curEgress.Name, groupSelector) + c.queue.Add(curEgress.Name) +} + +// deleteEgress processes Egress DELETE events and deletes corresponding EgressGroup. +func (c *EgressController) deleteEgress(obj interface{}) { + egress := obj.(*egressv1alpha2.Egress) + klog.Infof("Processing Egress %s DELETE event", egress.Name) + c.egressGroupStore.Delete(egress.Name) + // Unregister the group from the grouping interface. + c.groupingInterface.DeleteGroup(egressGroupType, egress.Name) +} diff --git a/pkg/controller/egress/controller_test.go b/pkg/controller/egress/controller_test.go new file mode 100644 index 00000000000..16ad0957324 --- /dev/null +++ b/pkg/controller/egress/controller_test.go @@ -0,0 +1,351 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package egress + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" + "github.com/vmware-tanzu/antrea/pkg/apis/crd/v1alpha2" + corev1a2 "github.com/vmware-tanzu/antrea/pkg/apis/crd/v1alpha2" + "github.com/vmware-tanzu/antrea/pkg/client/clientset/versioned" + fakeversioned "github.com/vmware-tanzu/antrea/pkg/client/clientset/versioned/fake" + crdinformers "github.com/vmware-tanzu/antrea/pkg/client/informers/externalversions" + "github.com/vmware-tanzu/antrea/pkg/controller/egress/store" + "github.com/vmware-tanzu/antrea/pkg/controller/grouping" +) + +var ( + node1 = "node1" + node2 = "node2" + node3 = "node3" + // Fake Pods + podFoo1 = newPod("default", "podFoo1", map[string]string{"app": "foo"}, node1, "1.1.1.1") + podFoo2 = newPod("default", "podFoo2", map[string]string{"app": "foo"}, node2, "1.1.2.1") + podBar1 = newPod("default", "podBar1", map[string]string{"app": "bar"}, node1, "1.1.1.2") + podFoo1InOtherNamespace = newPod("other", "podFoo1", map[string]string{"app": "foo"}, node1, "1.1.1.3") + podUnscheduled = newPod("default", "podUnscheduled", map[string]string{"app": "foo"}, "", "") + podNonIP = newPod("default", "podNonIP", map[string]string{"app": "foo"}, "node1", "") + // Fake Namespaces + nsDefault = newNamespace("default", map[string]string{"company": "default"}) + nsOther = newNamespace("other", map[string]string{"company": "other"}) +) + +func newNamespace(name string, labels map[string]string) *v1.Namespace { + return &v1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + } +} + +func newPod(namespace, name string, labels map[string]string, nodeName string, ip string) *v1.Pod { + pod := &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + Labels: labels, + }, + Spec: v1.PodSpec{ + NodeName: nodeName, + }, + } + if len(ip) > 0 { + pod.Status.PodIP = ip + pod.Status.PodIPs = []v1.PodIP{{IP: ip}} + } + return pod +} + +type egressController struct { + *EgressController + client kubernetes.Interface + crdClient versioned.Interface + informerFactory informers.SharedInformerFactory + crdInformerFactory crdinformers.SharedInformerFactory + groupingController *grouping.GroupEntityController +} + +// objects is an initial set of K8s objects that is exposed through the client. +func newController(objects ...runtime.Object) *egressController { + client := fake.NewSimpleClientset(objects...) + crdClient := fakeversioned.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, resyncPeriod) + crdInformerFactory := crdinformers.NewSharedInformerFactory(crdClient, resyncPeriod) + egressGroupStore := store.NewEgressGroupStore() + egressInformer := crdInformerFactory.Crd().V1alpha2().Egresses() + groupEntityIndex := grouping.NewGroupEntityIndex() + groupingController := grouping.NewGroupEntityController(groupEntityIndex, + informerFactory.Core().V1().Pods(), + informerFactory.Core().V1().Namespaces(), + crdInformerFactory.Crd().V1alpha2().ExternalEntities()) + controller := NewEgressController(groupEntityIndex, egressInformer, egressGroupStore) + return &egressController{ + controller, + client, + crdClient, + informerFactory, + crdInformerFactory, + groupingController, + } +} + +func TestAddEgress(t *testing.T) { + tests := []struct { + name string + inputEgress *v1alpha2.Egress + expectedEgressGroups map[string]*controlplane.EgressGroup + }{ + { + name: "Egress with podSelector and namespaceSelector", + inputEgress: &v1alpha2.Egress{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + Spec: v1alpha2.EgressSpec{ + AppliedTo: corev1a2.AppliedTo{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "foo"}, + }, + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: nsDefault.Labels, + }, + }, + EgressIP: "1.1.1.1", + }, + }, + expectedEgressGroups: map[string]*controlplane.EgressGroup{ + node1: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1.Name, Namespace: podFoo1.Namespace}}, + }, + }, + node2: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo2.Name, Namespace: podFoo2.Namespace}}, + }, + }, + node3: nil, + }, + }, + { + name: "Egress with namespaceSelector", + inputEgress: &v1alpha2.Egress{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + Spec: v1alpha2.EgressSpec{ + AppliedTo: corev1a2.AppliedTo{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: nsDefault.Labels, + }, + }, + EgressIP: "1.1.1.1", + }, + }, + expectedEgressGroups: map[string]*controlplane.EgressGroup{ + node1: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1.Name, Namespace: podFoo1.Namespace}}, + {Pod: &controlplane.PodReference{Name: podBar1.Name, Namespace: podBar1.Namespace}}, + }, + }, + node2: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo2.Name, Namespace: podFoo2.Namespace}}, + }, + }, + node3: nil, + }, + }, + { + name: "Egress with podSelector", + inputEgress: &v1alpha2.Egress{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + Spec: v1alpha2.EgressSpec{ + AppliedTo: corev1a2.AppliedTo{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "foo"}, + }, + }, + EgressIP: "1.1.1.1", + }, + }, + expectedEgressGroups: map[string]*controlplane.EgressGroup{ + node1: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1.Name, Namespace: podFoo1.Namespace}}, + {Pod: &controlplane.PodReference{Name: podFoo1InOtherNamespace.Name, Namespace: podFoo1InOtherNamespace.Namespace}}, + }, + }, + node2: { + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo2.Name, Namespace: podFoo2.Namespace}}, + }, + }, + node3: nil, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stopCh := make(chan struct{}) + defer close(stopCh) + var fakeObjects []runtime.Object + fakeObjects = append(fakeObjects, nsDefault, nsOther) + fakeObjects = append(fakeObjects, podFoo1, podFoo2, podBar1, podFoo1InOtherNamespace, podUnscheduled, podNonIP) + controller := newController(fakeObjects...) + controller.informerFactory.Start(stopCh) + controller.crdInformerFactory.Start(stopCh) + go controller.groupingController.Run(stopCh) + go controller.Run(stopCh) + + controller.crdClient.CrdV1alpha2().Egresses().Create(context.TODO(), tt.inputEgress, metav1.CreateOptions{}) + + for nodeName, expectedEgressGroup := range tt.expectedEgressGroups { + watcher, err := controller.egressGroupStore.Watch(context.TODO(), "", nil, fields.ParseSelectorOrDie(fmt.Sprintf("nodeName=%s", nodeName))) + assert.NoError(t, err) + gotEgressGroup := func() *controlplane.EgressGroup { + for { + select { + case <-stopCh: + return nil + case <-time.After(500 * time.Millisecond): + return nil + case event := <-watcher.ResultChan(): + if event.Type == watch.Added { + return event.Object.(*controlplane.EgressGroup) + } + } + } + }() + + if expectedEgressGroup == nil { + assert.Nil(t, gotEgressGroup) + } else { + require.NotNil(t, gotEgressGroup) + assert.Equal(t, expectedEgressGroup.ObjectMeta, gotEgressGroup.ObjectMeta) + assert.ElementsMatch(t, expectedEgressGroup.GroupMembers, gotEgressGroup.GroupMembers) + } + } + }) + } +} + +func TestUpdateEgress(t *testing.T) { + stopCh := make(chan struct{}) + defer close(stopCh) + controller := newController(nsDefault, podFoo1) + controller.informerFactory.Start(stopCh) + controller.crdInformerFactory.Start(stopCh) + go controller.groupingController.Run(stopCh) + go controller.Run(stopCh) + + egress := &v1alpha2.Egress{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + Spec: v1alpha2.EgressSpec{ + AppliedTo: corev1a2.AppliedTo{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "foo"}, + }, + }, + EgressIP: "1.1.1.1", + }, + } + controller.crdClient.CrdV1alpha2().Egresses().Create(context.TODO(), egress, metav1.CreateOptions{}) + + watcher, err := controller.egressGroupStore.Watch(context.TODO(), "", nil, fields.ParseSelectorOrDie(fmt.Sprintf("nodeName=%s", node1))) + assert.NoError(t, err) + + getEvent := func() *watch.Event { + for { + select { + case <-stopCh: + return nil + case <-time.After(500 * time.Millisecond): + return nil + case event := <-watcher.ResultChan(): + if event.Type != watch.Bookmark { + return &event + } + } + } + } + + assert.Equal(t, &watch.Event{ + Type: watch.Added, + Object: &controlplane.EgressGroup{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + GroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1.Name, Namespace: podFoo1.Namespace}}, + }, + }, + }, getEvent()) + + // Add a Pod matching the Egress's selector and running on this Node. + controller.client.CoreV1().Pods(podFoo1InOtherNamespace.Namespace).Create(context.TODO(), podFoo1InOtherNamespace, metav1.CreateOptions{}) + assert.Equal(t, &watch.Event{ + Type: watch.Modified, + Object: &controlplane.EgressGroupPatch{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + AddedGroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1InOtherNamespace.Name, Namespace: podFoo1InOtherNamespace.Namespace}}, + }, + }, + }, getEvent()) + + // Delete the above Pod. + controller.client.CoreV1().Pods(podFoo1InOtherNamespace.Namespace).Delete(context.TODO(), podFoo1InOtherNamespace.Name, metav1.DeleteOptions{}) + assert.Equal(t, &watch.Event{ + Type: watch.Modified, + Object: &controlplane.EgressGroupPatch{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + RemovedGroupMembers: []controlplane.GroupMember{ + {Pod: &controlplane.PodReference{Name: podFoo1InOtherNamespace.Name, Namespace: podFoo1InOtherNamespace.Namespace}}, + }, + }, + }, getEvent()) + + // Updating the Egress's spec to make it match no Pods on this Node. + egress.Spec.AppliedTo = corev1a2.AppliedTo{ + PodSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "non-existing-app"}, + }, + } + controller.crdClient.CrdV1alpha2().Egresses().Update(context.TODO(), egress, metav1.UpdateOptions{}) + assert.Equal(t, &watch.Event{ + Type: watch.Deleted, + Object: &controlplane.EgressGroup{ + ObjectMeta: metav1.ObjectMeta{Name: "egressA", UID: "uidA"}, + }, + }, getEvent()) +} diff --git a/pkg/controller/egress/store/egressgroup.go b/pkg/controller/egress/store/egressgroup.go new file mode 100644 index 00000000000..7befc58e57d --- /dev/null +++ b/pkg/controller/egress/store/egressgroup.go @@ -0,0 +1,196 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package store + +import ( + "fmt" + "reflect" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" + "github.com/vmware-tanzu/antrea/pkg/apiserver/storage" + "github.com/vmware-tanzu/antrea/pkg/apiserver/storage/ram" + "github.com/vmware-tanzu/antrea/pkg/controller/types" +) + +// egressGroupEvent implements storage.InternalEvent. +type egressGroupEvent struct { + // The current version of the stored EgressGroup. + CurrGroup *types.EgressGroup + // The previous version of the stored EgressGroup. + PrevGroup *types.EgressGroup + // The key of this EgressGroup. + Key string + ResourceVersion uint64 +} + +/// ToWatchEvent converts the egressGroupEvent to *watch.Event based on the provided Selectors. It has the following features: +// 1. Added event will be generated if the Selectors was not interested in the object but is now. +// 2. Modified event will be generated if the Selectors was and is interested in the object. +// 3. Deleted event will be generated if the Selectors was interested in the object but is not now. +// 4. If nodeName is specified, only GroupMembers that hosted by the Node will be in the event. +func (event *egressGroupEvent) ToWatchEvent(selectors *storage.Selectors, isInitEvent bool) *watch.Event { + prevObjSelected, currObjSelected := isSelected(event.Key, event.PrevGroup, event.CurrGroup, selectors, isInitEvent) + + // If nodeName is specified in selectors, only GroupMembers that hosted by the Node should be in the event. + nodeName, nodeSpecified := selectors.Field.RequiresExactMatch("nodeName") + + switch { + case !currObjSelected && !prevObjSelected: + // Watcher is not interested in that object. + return nil + case currObjSelected && !prevObjSelected: + // Watcher was not interested in that object but is now, an added event will be generated. + obj := new(controlplane.EgressGroup) + if nodeSpecified { + ToEgressGroupMsg(event.CurrGroup, obj, true, &nodeName) + } else { + ToEgressGroupMsg(event.CurrGroup, obj, true, nil) + } + return &watch.Event{Type: watch.Added, Object: obj} + case currObjSelected && prevObjSelected: + // Watcher was and is interested in that object, a modified event will be generated. + obj := new(controlplane.EgressGroupPatch) + obj.UID = event.CurrGroup.UID + obj.Name = event.CurrGroup.Name + + var currMembers, prevMembers controlplane.GroupMemberSet + if nodeSpecified { + currMembers = event.CurrGroup.GroupMemberByNode[nodeName] + prevMembers = event.PrevGroup.GroupMemberByNode[nodeName] + } else { + currMembers = controlplane.GroupMemberSet{} + for _, members := range event.CurrGroup.GroupMemberByNode { + currMembers = currMembers.Union(members) + } + prevMembers = controlplane.GroupMemberSet{} + for _, members := range event.PrevGroup.GroupMemberByNode { + prevMembers = prevMembers.Union(members) + } + } + for _, member := range currMembers.Difference(prevMembers) { + obj.AddedGroupMembers = append(obj.AddedGroupMembers, *member) + } + for _, member := range prevMembers.Difference(currMembers) { + obj.RemovedGroupMembers = append(obj.RemovedGroupMembers, *member) + } + + if len(obj.AddedGroupMembers)+len(obj.RemovedGroupMembers) == 0 { + // No change for the watcher. + return nil + } + return &watch.Event{Type: watch.Modified, Object: obj} + case !currObjSelected && prevObjSelected: + // Watcher was interested in that object but is not interested now, a deleted event will be generated. + obj := new(controlplane.EgressGroup) + if nodeSpecified { + ToEgressGroupMsg(event.PrevGroup, obj, false, &nodeName) + } else { + ToEgressGroupMsg(event.PrevGroup, obj, false, nil) + } + return &watch.Event{Type: watch.Deleted, Object: obj} + } + return nil +} + +func (event *egressGroupEvent) GetResourceVersion() uint64 { + return event.ResourceVersion +} + +var _ storage.GenEventFunc = genEgressGroupEvent + +// genEgressGroupEvent generates InternalEvent from the given versions of an EgressGroup. +func genEgressGroupEvent(key string, prevObj, currObj interface{}, rv uint64) (storage.InternalEvent, error) { + if reflect.DeepEqual(prevObj, currObj) { + return nil, nil + } + + event := &egressGroupEvent{Key: key, ResourceVersion: rv} + + if prevObj != nil { + event.PrevGroup = prevObj.(*types.EgressGroup) + } + if currObj != nil { + event.CurrGroup = currObj.(*types.EgressGroup) + } + + return event, nil +} + +// ToEgressGroupMsg converts the stored EgressGroup to its message form. +// If includeBody is true, GroupMembers will be copied. +// If nodeName is provided, only GroupMembers that hosted by the Node will be copied. +func ToEgressGroupMsg(in *types.EgressGroup, out *controlplane.EgressGroup, includeBody bool, nodeName *string) { + out.Name = in.Name + out.UID = in.UID + if !includeBody || in.GroupMemberByNode == nil { + return + } + if nodeName != nil { + if members, exists := in.GroupMemberByNode[*nodeName]; exists { + for _, member := range members { + out.GroupMembers = append(out.GroupMembers, *member) + } + } + } else { + for _, members := range in.GroupMemberByNode { + for _, member := range members { + out.GroupMembers = append(out.GroupMembers, *member) + } + } + } +} + +// EgressGroupKeyFunc knows how to get the key of an EgressGroup. +func EgressGroupKeyFunc(obj interface{}) (string, error) { + group, ok := obj.(*types.EgressGroup) + if !ok { + return "", fmt.Errorf("object is not *types.EgressGroup: %v", obj) + } + return group.Name, nil +} + +// NewEgressGroupStore creates a store of EgressGroup. +func NewEgressGroupStore() storage.Interface { + return ram.NewStore(EgressGroupKeyFunc, nil, genEgressGroupEvent, keyAndSpanSelectFunc, func() runtime.Object { return new(controlplane.EgressGroup) }) +} + +// keyAndSpanSelectFunc returns whether the provided selectors matches the key and/or the nodeNames. +func keyAndSpanSelectFunc(selectors *storage.Selectors, key string, obj interface{}) bool { + // If Key is present in selectors, the provided key must match it. + if selectors.Key != "" && key != selectors.Key { + return false + } + // If nodeName is present in selectors's Field selector, the provided nodeNames must contain it. + if nodeName, found := selectors.Field.RequiresExactMatch("nodeName"); found { + if !obj.(types.Span).Has(nodeName) { + return false + } + } + return true +} + +// isSelected determines if the previous and the current version of an object should be selected by the given selectors. +func isSelected(key string, prevObj, currObj interface{}, selectors *storage.Selectors, isInitEvent bool) (bool, bool) { + // We have filtered out init events that we are not interested in, so the current object must be selected. + if isInitEvent { + return false, true + } + prevObjSelected := !reflect.ValueOf(prevObj).IsNil() && keyAndSpanSelectFunc(selectors, key, prevObj) + currObjSelected := !reflect.ValueOf(currObj).IsNil() && keyAndSpanSelectFunc(selectors, key, currObj) + return prevObjSelected, currObjSelected +} diff --git a/pkg/controller/types/egress.go b/pkg/controller/types/egress.go new file mode 100644 index 00000000000..d8b12358973 --- /dev/null +++ b/pkg/controller/types/egress.go @@ -0,0 +1,33 @@ +// Copyright 2021 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "k8s.io/apimachinery/pkg/types" + + "github.com/vmware-tanzu/antrea/pkg/apis/controlplane" +) + +// EgressGroup describes a set of GroupMembers to apply Egress to. +type EgressGroup struct { + SpanMeta + // UID of this EgressGroup, it's same as the UID of the Egress. + UID types.UID + // Name of this EgressGroup, it's same as the name of the Egress. + Name string + // GroupMemberByNode is a mapping from nodeName to a set of GroupMembers on the Node. + // It will be converted to a slice of GroupMember for transferring according to client's selection. + GroupMemberByNode map[string]controlplane.GroupMemberSet +} diff --git a/pkg/features/antrea_features.go b/pkg/features/antrea_features.go index a8e4aca2370..4edd3065517 100644 --- a/pkg/features/antrea_features.go +++ b/pkg/features/antrea_features.go @@ -61,6 +61,10 @@ const ( // alpha: v0.13 // Expose Pod ports through NodePort NodePortLocal featuregate.Feature = "NodePortLocal" + + // alpha: v1.0 + // Enable controlling SNAT IPs of Pod egress traffic. + Egress featuregate.Feature = "Egress" ) var ( @@ -77,6 +81,7 @@ var ( defaultAntreaFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{ AntreaPolicy: {Default: false, PreRelease: featuregate.Alpha}, AntreaProxy: {Default: true, PreRelease: featuregate.Beta}, + Egress: {Default: false, PreRelease: featuregate.Alpha}, EndpointSlice: {Default: false, PreRelease: featuregate.Alpha}, Traceflow: {Default: true, PreRelease: featuregate.Beta}, FlowExporter: {Default: false, PreRelease: featuregate.Alpha},