Skip to content

Commit

Permalink
sidecarSet support namespace selector
Browse files Browse the repository at this point in the history
Signed-off-by: liheng.zms <[email protected]>
  • Loading branch information
zmberg committed Feb 16, 2023
1 parent 83029a4 commit 4480de4
Show file tree
Hide file tree
Showing 11 changed files with 585 additions and 54 deletions.
4 changes: 4 additions & 0 deletions apis/apps/v1alpha1/sidecarset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type SidecarSetSpec struct {
// otherwise, match pods in all namespaces(in cluster)
Namespace string `json:"namespace,omitempty"`

// NamespaceSelector selector which namespaces inject sidecar containers.
// Default to the empty LabelSelector, which matches everything.
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`

// InitContainers is the list of init containers to be injected into the selected pod
// We will inject those containers by their name in ascending order
// We only inject init containers when a new pod is created, it does not apply to any existing pod
Expand Down
5 changes: 5 additions & 0 deletions apis/apps/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions config/crd/bases/apps.kruise.io_sidecarsets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,51 @@ spec:
description: Namespace sidecarSet will only match the pods in the
namespace otherwise, match pods in all namespaces(in cluster)
type: string
namespaceSelector:
description: NamespaceSelector selector which namespaces inject sidecar
containers. Default to the empty LabelSelector, which matches everything.
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements.
The requirements are ANDed.
items:
description: A label selector requirement is a selector that
contains values, a key, and an operator that relates the key
and values.
properties:
key:
description: key is the label key that the selector applies
to.
type: string
operator:
description: operator represents a key's relationship to
a set of values. Valid operators are In, NotIn, Exists
and DoesNotExist.
type: string
values:
description: values is an array of string values. If the
operator is In or NotIn, the values array must be non-empty.
If the operator is Exists or DoesNotExist, the values
array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
required:
- key
- operator
type: object
type: array
matchLabels:
additionalProperties:
type: string
description: matchLabels is a map of {key,value} pairs. A single
{key,value} in the matchLabels map is equivalent to an element
of matchExpressions, whose key field is "key", the operator
is "In", and the values array contains only "value". The requirements
are ANDed.
type: object
type: object
patchPodMetadata:
description: SidecarSet support to inject & in-place update metadata
in pod.
Expand Down
44 changes: 40 additions & 4 deletions pkg/control/sidecarcontrol/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ limitations under the License.
package sidecarcontrol

import (
"context"
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"

utilclient "github.com/openkruise/kruise/pkg/util/client"

jsonpatch "github.com/evanphx/json-patch"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/features"
Expand Down Expand Up @@ -76,11 +79,22 @@ type SidecarSetUpgradeSpec struct {
}

// PodMatchSidecarSet determines if pod match Selector of sidecar.
func PodMatchedSidecarSet(pod *corev1.Pod, sidecarSet appsv1alpha1.SidecarSet) (bool, error) {
//If matchedNamespace is not empty, sidecarSet will only match the pods in the namespace
if sidecarSet.Spec.Namespace != "" && sidecarSet.Spec.Namespace != pod.Namespace {
return false, nil
func PodMatchedSidecarSet(c client.Client, pod *corev1.Pod, sidecarSet *appsv1alpha1.SidecarSet) (bool, error) {
podNamespace := pod.Namespace
if podNamespace == "" {
podNamespace = "default"
}
//If Namespace is not empty, sidecarSet will only match the pods in the namespaces
if sidecarSet.Spec.Namespace != "" || sidecarSet.Spec.NamespaceSelector != nil {
ns, err := FetchSidecarSetMatchedNamespace(c, sidecarSet)
if err != nil {
return false, err
}
if !ns.Has(podNamespace) {
return false, nil
}
}

// if selector not matched, then continue
selector, err := util.ValidatedLabelSelectorAsSelector(sidecarSet.Spec.Selector)
if err != nil {
Expand All @@ -93,6 +107,28 @@ func PodMatchedSidecarSet(pod *corev1.Pod, sidecarSet appsv1alpha1.SidecarSet) (
return false, nil
}

// FetchSidecarSetMatchedNamespace fetch sidecarSet matched namespaces
func FetchSidecarSetMatchedNamespace(c client.Client, sidecarSet *appsv1alpha1.SidecarSet) (sets.String, error) {
ns := sets.NewString()
//If Namespace is not empty, sidecarSet will only match the pods in the namespaces
if sidecarSet.Spec.Namespace != "" {
return ns.Insert(sidecarSet.Spec.Namespace), nil
}
// get more faster selector
selector, err := util.ValidatedLabelSelectorAsSelector(sidecarSet.Spec.NamespaceSelector)
if err != nil {
return nil, err
}
nsList := &corev1.NamespaceList{}
if err = c.List(context.TODO(), nsList, &client.ListOptions{LabelSelector: selector}, utilclient.DisableDeepCopy); err != nil {
return nil, err
}
for _, obj := range nsList.Items {
ns.Insert(obj.Name)
}
return ns, nil
}

// IsActivePod determines the pod whether need be injected and updated
func IsActivePod(pod *corev1.Pod) bool {
/*for _, namespace := range SidecarIgnoredNamespaces {
Expand Down
203 changes: 203 additions & 0 deletions pkg/control/sidecarcontrol/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,3 +1059,206 @@ func TestValidateSidecarSetPatchMetadataWhitelist(t *testing.T) {
})
}
}

func TestPodMatchedSidecarSet(t *testing.T) {
cases := []struct {
name string
getSidecarSet func() *appsv1alpha1.SidecarSet
getPod func() *corev1.Pod
getNs func() []*corev1.Namespace
expect bool
}{
{
name: "test1",
getSidecarSet: func() *appsv1alpha1.SidecarSet {
demo := &appsv1alpha1.SidecarSet{
ObjectMeta: metav1.ObjectMeta{Name: "sidecarset-test"},
Spec: appsv1alpha1.SidecarSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "nginx"},
},
},
}
return demo
},
getPod: func() *corev1.Pod {
demo := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{"app": "nginx"},
Namespace: "app1",
},
}
return demo
},
getNs: func() []*corev1.Namespace {
return nil
},
expect: true,
},
{
name: "test2",
getSidecarSet: func() *appsv1alpha1.SidecarSet {
demo := &appsv1alpha1.SidecarSet{
ObjectMeta: metav1.ObjectMeta{Name: "sidecarset-test"},
Spec: appsv1alpha1.SidecarSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "nginx"},
},
Namespace: "app1",
},
}
return demo
},
getPod: func() *corev1.Pod {
demo := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{"app": "nginx"},
Namespace: "app1",
},
}
return demo
},
getNs: func() []*corev1.Namespace {
return nil
},
expect: true,
},
{
name: "test3",
getSidecarSet: func() *appsv1alpha1.SidecarSet {
demo := &appsv1alpha1.SidecarSet{
ObjectMeta: metav1.ObjectMeta{Name: "sidecarset-test"},
Spec: appsv1alpha1.SidecarSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "nginx"},
},
Namespace: "app2",
},
}
return demo
},
getPod: func() *corev1.Pod {
demo := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{"app": "nginx"},
Namespace: "app1",
},
}
return demo
},
getNs: func() []*corev1.Namespace {
return nil
},
expect: false,
},
{
name: "test4",
getSidecarSet: func() *appsv1alpha1.SidecarSet {
demo := &appsv1alpha1.SidecarSet{
ObjectMeta: metav1.ObjectMeta{Name: "sidecarset-test"},
Spec: appsv1alpha1.SidecarSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "nginx"},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "app1"},
},
},
}
return demo
},
getPod: func() *corev1.Pod {
demo := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{"app": "nginx"},
Namespace: "app1",
},
}
return demo
},
getNs: func() []*corev1.Namespace {
demo := []*corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app1",
Labels: map[string]string{"app": "app1"},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "app2",
Labels: map[string]string{"app": "app2"},
},
},
}
return demo
},
expect: true,
},
{
name: "test5",
getSidecarSet: func() *appsv1alpha1.SidecarSet {
demo := &appsv1alpha1.SidecarSet{
ObjectMeta: metav1.ObjectMeta{Name: "sidecarset-test"},
Spec: appsv1alpha1.SidecarSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "nginx"},
},
NamespaceSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": "app2"},
},
},
}
return demo
},
getPod: func() *corev1.Pod {
demo := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Labels: map[string]string{"app": "nginx"},
Namespace: "app1",
},
}
return demo
},
getNs: func() []*corev1.Namespace {
demo := []*corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "app1",
Labels: map[string]string{"app": "app1"},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "app2",
Labels: map[string]string{"app": "app2"},
},
},
}
return demo
},
expect: false,
},
}

for _, cs := range cases {
t.Run(cs.name, func(t *testing.T) {
fakeClient := fake.NewClientBuilder().WithScheme(sch).Build()
for _, ns := range cs.getNs() {
_ = fakeClient.Create(context.TODO(), ns)
}
matched, err := PodMatchedSidecarSet(fakeClient, cs.getPod(), cs.getSidecarSet())
if err != nil {
t.Fatalf("PodMatchedSidecarSet failed: %s", err.Error())
}
if cs.expect != matched {
t.Fatalf("expect(%v), but get(%v)", cs.expect, matched)
}
})
}
}
19 changes: 13 additions & 6 deletions pkg/controller/sidecarset/sidecarset_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func (p *Processor) listMatchedSidecarSets(pod *corev1.Pod) string {
//matched SidecarSet.Name list
sidecarSetNames := make([]string, 0)
for _, sidecarSet := range sidecarSetList.Items {
if matched, _ := sidecarcontrol.PodMatchedSidecarSet(pod, sidecarSet); matched {
if matched, _ := sidecarcontrol.PodMatchedSidecarSet(p.Client, pod, &sidecarSet); matched {
sidecarSetNames = append(sidecarSetNames, sidecarSet.Name)
}
}
Expand Down Expand Up @@ -260,9 +260,16 @@ func (p *Processor) getMatchingPods(s *appsv1alpha1.SidecarSet) ([]*corev1.Pod,
if err != nil {
return nil, err
}

// If sidecarSet.Spec.Namespace is empty, then select in cluster
scopedNamespaces := []string{s.Spec.Namespace}
scopedNamespaces := sets.NewString()
if s.Spec.Namespace != "" || s.Spec.NamespaceSelector != nil {
if scopedNamespaces, err = sidecarcontrol.FetchSidecarSetMatchedNamespace(p.Client, s); err != nil {
return nil, err
}
// If sidecarSet.Spec.Namespace is empty, then select in cluster
} else {
// when namespace="", client will list pods in all namespaces
scopedNamespaces.Insert("")
}
selectedPods, err := p.getSelectedPods(scopedNamespaces, selector)
if err != nil {
return nil, err
Expand All @@ -282,10 +289,10 @@ func (p *Processor) getMatchingPods(s *appsv1alpha1.SidecarSet) ([]*corev1.Pod,
}

// get selected pods(DisableDeepCopy:true, indicates must be deep copy before update pod objection)
func (p *Processor) getSelectedPods(namespaces []string, selector labels.Selector) (relatedPods []*corev1.Pod, err error) {
func (p *Processor) getSelectedPods(namespaces sets.String, selector labels.Selector) (relatedPods []*corev1.Pod, err error) {
// DisableDeepCopy:true, indicates must be deep copy before update pod objection
listOpts := &client.ListOptions{LabelSelector: selector}
for _, ns := range namespaces {
for _, ns := range namespaces.List() {
allPods := &corev1.PodList{}
listOpts.Namespace = ns
if listErr := p.Client.List(context.TODO(), allPods, listOpts, utilclient.DisableDeepCopy); listErr != nil {
Expand Down
Loading

0 comments on commit 4480de4

Please sign in to comment.