* Module name changed to `github.com/rajch/weave` * Added environment variable `CHECKPOINT_DISABLE=1` to default manifests, to bypass weaveworks telemetry
268 lines
8.4 KiB
Go
268 lines
8.4 KiB
Go
package npc
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
|
"github.com/rajch/weave/common"
|
|
"github.com/rajch/weave/net/ipset"
|
|
)
|
|
|
|
type selectorSpec struct {
|
|
key string // string representation (for hash keying/equality comparison)
|
|
podSelector labels.Selector // k8s Selector object (for matching pods)
|
|
namespaceSelector labels.Selector // k8s Selector object (for matching namespaces)
|
|
policyTypes []policyType // If non-empty, then selectorSpec is a target selector for given policyTypes.
|
|
|
|
ipsetType ipset.Type // type of ipset to provision
|
|
ipsetName ipset.Name // generated ipset name
|
|
nsName string // Namespace name
|
|
}
|
|
|
|
func newSelectorSpec(podJSON *metav1.LabelSelector, namespaceJSON *metav1.LabelSelector, policyType []policyType, nsName string, ipsetType ipset.Type) (*selectorSpec, error) {
|
|
var podSelector, namespaceSelector labels.Selector
|
|
var err error
|
|
var key string
|
|
if namespaceJSON != nil {
|
|
namespaceSelector, err = metav1.LabelSelectorAsSelector(namespaceJSON)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key = namespaceSelector.String()
|
|
}
|
|
if podJSON != nil {
|
|
podSelector, err = metav1.LabelSelectorAsSelector(podJSON)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key = key + podSelector.String()
|
|
}
|
|
return &selectorSpec{
|
|
key: key,
|
|
podSelector: podSelector,
|
|
namespaceSelector: namespaceSelector,
|
|
policyTypes: policyType,
|
|
// We prefix the selector string with the namespace name when generating
|
|
// the shortname because you can specify the same selector in multiple
|
|
// namespaces - we need those to map to distinct ipsets
|
|
ipsetName: ipset.Name(IpsetNamePrefix + shortName(nsName+":"+key)),
|
|
ipsetType: ipsetType,
|
|
nsName: nsName}, nil
|
|
}
|
|
|
|
func (spec *selectorSpec) getRuleSpec(src bool) ([]string, string) {
|
|
dir := "dst"
|
|
if src {
|
|
dir = "src"
|
|
}
|
|
rule := []string{"-m", "set", "--match-set", string(spec.ipsetName), dir}
|
|
|
|
var comment string
|
|
if spec.nsName != "" {
|
|
comment = fmt.Sprintf("pods: namespace: %s, selector: %s", spec.nsName, spec.key)
|
|
} else {
|
|
comment = fmt.Sprintf("namespaces: selector: %s", spec.key)
|
|
}
|
|
|
|
return rule, comment
|
|
}
|
|
|
|
type selector struct {
|
|
ips ipset.Interface
|
|
spec *selectorSpec
|
|
}
|
|
|
|
func (s *selector) matchesPodSelector(podsLabelMap map[string]string) bool {
|
|
return s.spec.podSelector.Matches(labels.Set(podsLabelMap))
|
|
}
|
|
|
|
func (s *selector) matchesNamespaceSelector(namespaceLabelMap map[string]string) bool {
|
|
return s.spec.namespaceSelector.Matches(labels.Set(namespaceLabelMap))
|
|
}
|
|
|
|
func (s *selector) matchesNamespacedPodSelector(podsLabelMap, namespaceLabelMap map[string]string) bool {
|
|
return s.spec.podSelector.Matches(labels.Set(podsLabelMap)) && s.spec.namespaceSelector.Matches(labels.Set(namespaceLabelMap))
|
|
}
|
|
|
|
func (s *selector) addEntry(user ipset.UID, entry string, comment string) error {
|
|
return s.ips.AddEntry(user, s.spec.ipsetName, entry, comment)
|
|
}
|
|
|
|
func (s *selector) delEntry(user ipset.UID, entry string) error {
|
|
return s.ips.DelEntry(user, s.spec.ipsetName, entry)
|
|
}
|
|
|
|
type selectorFn func(selector *selector) error
|
|
type selectorWithPolicyTypeFn func(selector *selector, policyType policyType) error
|
|
|
|
type selectorSet struct {
|
|
ips ipset.Interface
|
|
onNewSelector selectorFn
|
|
|
|
// invoked after target selector has been provisioned for the first time
|
|
onNewTargetSelector selectorWithPolicyTypeFn
|
|
// invoked after the last instance of target selector has been deprovisioned
|
|
onDestroyTargetSelector selectorWithPolicyTypeFn
|
|
|
|
users map[string]map[ipset.UID]struct{} // list of users per selector
|
|
entries map[string]*selector
|
|
|
|
// We need to keep track of target selector instances to be able to invoke
|
|
// onNewTargetSelector and onDestroyTargetSelector callbacks at the right time;
|
|
// selectorSpec.Key -> policyType -> count
|
|
targetSelectorsCount map[string]map[policyType]int
|
|
}
|
|
|
|
func newSelectorSet(ips ipset.Interface, onNewSelector selectorFn, onNewTargetSelector, onDestroyTargetSelector selectorWithPolicyTypeFn) *selectorSet {
|
|
return &selectorSet{
|
|
ips: ips,
|
|
onNewSelector: onNewSelector,
|
|
onNewTargetSelector: onNewTargetSelector,
|
|
onDestroyTargetSelector: onDestroyTargetSelector,
|
|
users: make(map[string]map[ipset.UID]struct{}),
|
|
entries: make(map[string]*selector),
|
|
targetSelectorsCount: make(map[string]map[policyType]int)}
|
|
}
|
|
|
|
func (ss *selectorSet) addToMatchingPodSelector(user ipset.UID, podLabelsMap map[string]string, entry string, comment string) (bool, bool, error) {
|
|
foundIngress := false
|
|
foundEgress := false
|
|
for _, s := range ss.entries {
|
|
if s.matchesPodSelector(podLabelsMap) {
|
|
if ss.targetSelectorExist(s, policyTypeIngress) {
|
|
foundIngress = true
|
|
}
|
|
if ss.targetSelectorExist(s, policyTypeEgress) {
|
|
foundEgress = true
|
|
}
|
|
if err := s.addEntry(user, entry, comment); err != nil {
|
|
return foundIngress, foundEgress, err
|
|
}
|
|
}
|
|
}
|
|
return foundIngress, foundEgress, nil
|
|
}
|
|
|
|
func (ss *selectorSet) addToMatchingNamespaceSelector(user ipset.UID, namespaceLabelsMap map[string]string, entry string, comment string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matchesNamespaceSelector(namespaceLabelsMap) {
|
|
if err := s.addEntry(user, entry, comment); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) addToMatchingNamespacedPodSelector(user ipset.UID, podLabelsMap map[string]string, namespaceLabelsMap map[string]string, entry string, comment string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matchesNamespacedPodSelector(podLabelsMap, namespaceLabelsMap) {
|
|
if err := s.addEntry(user, entry, comment); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) delFromMatchingPodSelector(user ipset.UID, podLabelsMap map[string]string, entry string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matchesPodSelector(podLabelsMap) {
|
|
if err := s.delEntry(user, entry); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) delFromMatchingNamespaceSelector(user ipset.UID, namespaceLabelsMap map[string]string, entry string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matchesNamespaceSelector(namespaceLabelsMap) {
|
|
if err := s.delEntry(user, entry); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) delFromMatchingNamespacedPodSelector(user ipset.UID, podLabelsMap map[string]string, namespaceLabelsMap map[string]string, entry string) error {
|
|
for _, s := range ss.entries {
|
|
if s.matchesNamespacedPodSelector(podLabelsMap, namespaceLabelsMap) {
|
|
if err := s.delEntry(user, entry); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) targetSelectorExist(s *selector, policyType policyType) bool {
|
|
return ss.targetSelectorsCount[s.spec.key][policyType] > 0
|
|
}
|
|
|
|
func (ss *selectorSet) deprovision(user ipset.UID, current, desired map[string]*selectorSpec) error {
|
|
for key, spec := range current {
|
|
if _, found := desired[key]; !found {
|
|
delete(ss.users[key], user)
|
|
if len(ss.users[key]) == 0 {
|
|
common.Log.Infof("destroying ipset: %#v", spec)
|
|
if err := ss.ips.Destroy(spec.ipsetName); err != nil {
|
|
return err
|
|
}
|
|
|
|
delete(ss.entries, key)
|
|
delete(ss.users, key)
|
|
}
|
|
|
|
for _, policyType := range spec.policyTypes {
|
|
ss.targetSelectorsCount[key][policyType]--
|
|
if ss.targetSelectorsCount[key][policyType] == 0 {
|
|
if err := ss.onDestroyTargetSelector(&selector{ss.ips, spec}, policyType); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// TODO(brb) delete(...)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ss *selectorSet) provision(user ipset.UID, current, desired map[string]*selectorSpec) error {
|
|
for key, spec := range desired {
|
|
if _, found := current[key]; !found {
|
|
selector := &selector{ss.ips, spec}
|
|
|
|
if _, found := ss.users[key]; !found {
|
|
common.Log.Infof("creating ipset: %#v", spec)
|
|
if err := ss.ips.Create(spec.ipsetName, spec.ipsetType); err != nil {
|
|
return err
|
|
}
|
|
if err := ss.onNewSelector(selector); err != nil {
|
|
return err
|
|
}
|
|
ss.users[key] = make(map[ipset.UID]struct{})
|
|
ss.entries[key] = selector
|
|
}
|
|
ss.users[key][user] = struct{}{}
|
|
|
|
for _, pt := range spec.policyTypes {
|
|
if _, found := ss.targetSelectorsCount[key]; !found {
|
|
ss.targetSelectorsCount[key] = make(map[policyType]int)
|
|
}
|
|
ss.targetSelectorsCount[key][pt]++
|
|
if ss.targetSelectorsCount[key][pt] == 1 {
|
|
if err := ss.onNewTargetSelector(selector, pt); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|