Skip to content

Commit

Permalink
sidecarSet support namespace selector (#1178)
Browse files Browse the repository at this point in the history
Signed-off-by: liheng.zms <[email protected]>
  • Loading branch information
zmberg authored Feb 28, 2023
1 parent e70c146 commit 1b3641d
Show file tree
Hide file tree
Showing 11 changed files with 603 additions and 60 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 select which namespaces to 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 select which namespaces to 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
52 changes: 49 additions & 3 deletions pkg/control/sidecarcontrol/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package sidecarcontrol

import (
"context"
"encoding/json"
"fmt"
"reflect"
Expand All @@ -27,6 +28,7 @@ import (
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/features"
"github.com/openkruise/kruise/pkg/util"
utilclient "github.com/openkruise/kruise/pkg/util/client"
"github.com/openkruise/kruise/pkg/util/configuration"
utilfeature "github.com/openkruise/kruise/pkg/util/feature"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -76,11 +78,20 @@ 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 {
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.Namespace != podNamespace {
return false, nil
}
if sidecarSet.Spec.NamespaceSelector != nil &&
!IsSelectorNamespace(c, podNamespace, sidecarSet.Spec.NamespaceSelector) {
return false, nil
}

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

func IsSelectorNamespace(c client.Client, ns string, nsSelector *metav1.LabelSelector) bool {
selector, err := util.ValidatedLabelSelectorAsSelector(nsSelector)
if err != nil {
return false
}
nsObj := &corev1.Namespace{}
err = c.Get(context.TODO(), client.ObjectKey{Name: ns}, nsObj)
if err != nil {
return false
}
return selector.Matches(labels.Set(nsObj.Labels))
}

// 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)
}
})
}
}
Loading

0 comments on commit 1b3641d

Please sign in to comment.