diff --git a/charts/vela-core/templates/defwithtemplate/cron-task.yaml b/charts/vela-core/templates/defwithtemplate/cron-task.yaml index 718aa1a04..5083ea5cd 100644 --- a/charts/vela-core/templates/defwithtemplate/cron-task.yaml +++ b/charts/vela-core/templates/defwithtemplate/cron-task.yaml @@ -46,6 +46,13 @@ spec: activeDeadlineSeconds: parameter.activeDeadlineSeconds } backoffLimit: parameter.backoffLimit + selector: matchLabels: { + if parameter.labels != _|_ { + parameter.labels + } + "app.oam.dev/name": context.appName + "app.oam.dev/component": context.name + } template: { metadata: { labels: { diff --git a/charts/vela-minimal/templates/defwithtemplate/cron-task.yaml b/charts/vela-minimal/templates/defwithtemplate/cron-task.yaml index 718aa1a04..5083ea5cd 100644 --- a/charts/vela-minimal/templates/defwithtemplate/cron-task.yaml +++ b/charts/vela-minimal/templates/defwithtemplate/cron-task.yaml @@ -46,6 +46,13 @@ spec: activeDeadlineSeconds: parameter.activeDeadlineSeconds } backoffLimit: parameter.backoffLimit + selector: matchLabels: { + if parameter.labels != _|_ { + parameter.labels + } + "app.oam.dev/name": context.appName + "app.oam.dev/component": context.name + } template: { metadata: { labels: { diff --git a/pkg/velaql/providers/query/tree.go b/pkg/velaql/providers/query/tree.go index 94fafce92..2c387c71d 100644 --- a/pkg/velaql/providers/query/tree.go +++ b/pkg/velaql/providers/query/tree.go @@ -206,6 +206,15 @@ func init() { DefaultGenListOptionFunc: kustomization2AnyListOption, DisableFilterByOwnerReference: true, }, + ChildrenResourcesRule{ + SubResources: buildSubResources([]*SubResourceSelector{ + { + ResourceType: ResourceType{APIVersion: "v1", Kind: "Pod"}, + listOptions: cronJobLabelListOption, + }, + }), + GroupResourceType: GroupResourceType{Group: "batch/v1", Kind: "CronJob"}, + }, ) } @@ -292,9 +301,9 @@ type WorkloadUnstructured struct { unstructured.Unstructured } -// GetSelector get the selector from the field path: spec.selector -func (w *WorkloadUnstructured) GetSelector() (labels.Selector, error) { - value, exist, err := unstructured.NestedFieldNoCopy(w.Object, "spec", "selector") +// GetSelector get the selector from the field path +func (w *WorkloadUnstructured) GetSelector(fields ...string) (labels.Selector, error) { + value, exist, err := unstructured.NestedFieldNoCopy(w.Object, fields...) if err != nil { return nil, err } @@ -313,7 +322,7 @@ func (w *WorkloadUnstructured) GetSelector() (labels.Selector, error) { var defaultWorkloadLabelListOption genListOptionFunc = func(obj unstructured.Unstructured) (client.ListOptions, error) { workload := WorkloadUnstructured{obj} - deploySelector, err := workload.GetSelector() + deploySelector, err := workload.GetSelector("spec", "selector") if err != nil { return client.ListOptions{}, err } @@ -333,6 +342,15 @@ var service2EndpointListOption = func(obj unstructured.Unstructured) (client.Lis return client.ListOptions{Namespace: svc.Namespace, LabelSelector: stsSelector}, nil } +var cronJobLabelListOption = func(obj unstructured.Unstructured) (client.ListOptions, error) { + workload := WorkloadUnstructured{obj} + cronJobSelector, err := workload.GetSelector("spec", "jobTemplate", "spec", "selector") + if err != nil { + return client.ListOptions{}, err + } + return client.ListOptions{Namespace: obj.GetNamespace(), LabelSelector: cronJobSelector}, nil +} + var helmRelease2AnyListOption = func(obj unstructured.Unstructured) (client.ListOptions, error) { hrSelector, err := v1.LabelSelectorAsSelector(&v1.LabelSelector{MatchLabels: map[string]string{ "helm.toolkit.fluxcd.io/name": obj.GetName(), diff --git a/pkg/velaql/providers/query/tree_test.go b/pkg/velaql/providers/query/tree_test.go index 40ab8830d..2c2fca9a0 100644 --- a/pkg/velaql/providers/query/tree_test.go +++ b/pkg/velaql/providers/query/tree_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer/yaml" types2 "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" @@ -201,6 +202,17 @@ func TestService2EndpointOption(t *testing.T) { assert.Equal(t, "service-name=test,uid=test-uid", l.LabelSelector.String()) } +func TestCronJobLabelListOption(t *testing.T) { + // convert yaml to unstructured + obj := unstructured.Unstructured{} + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, _, err := dec.Decode([]byte(cronJob), nil, &obj) + assert.NoError(t, err) + l, err := cronJobLabelListOption(obj) + assert.NoError(t, err) + assert.Equal(t, "app=cronJob1", l.LabelSelector.String()) +} + func TestServiceStatus(t *testing.T) { lbHealthSvc := v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, Status: v1.ServiceStatus{ LoadBalancer: v1.LoadBalancerStatus{ @@ -1212,9 +1224,30 @@ var _ = Describe("unit-test to e2e test", func() { }, }, } + pod5 := v1.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "pod5", + Namespace: "test-namespace", + Labels: map[string]string{ + "app": "cronJob1", + }, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Image: "nginx", + Name: "nginx", + }, + }, + }, + } var objectList []client.Object - objectList = append(objectList, &deploy1, &deploy1, &rs1, &rs2, &rs3, &rs4, &pod1, &pod2, &pod3, &rs4, &pod4) + objectList = append(objectList, &deploy1, &deploy1, &rs1, &rs2, &rs3, &rs4, &pod1, &pod2, &pod3, &rs4, &pod4, &pod5) BeforeEach(func() { Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) Expect(k8sClient.Create(ctx, deploy1.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) @@ -1225,6 +1258,7 @@ var _ = Describe("unit-test to e2e test", func() { Expect(k8sClient.Create(ctx, pod1.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) Expect(k8sClient.Create(ctx, pod2.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) Expect(k8sClient.Create(ctx, pod3.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) + Expect(k8sClient.Create(ctx, pod5.DeepCopy())).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) cRs4 := rs4.DeepCopy() Expect(k8sClient.Create(ctx, cRs4)).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{})) @@ -1285,6 +1319,15 @@ var _ = Describe("unit-test to e2e test", func() { nil, nil, true) Expect(err).Should(BeNil()) Expect(len(items3)).Should(BeEquivalentTo(1)) + + u4 := unstructured.Unstructured{} + dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) + _, _, err = dec.Decode([]byte(cronJob), nil, &u4) + Expect(err).Should(BeNil()) + item4, err := listItemByRule(ctx, k8sClient, ResourceType{APIVersion: "v1", Kind: "Pod"}, u4, + cronJobLabelListOption, nil, true) + Expect(err).Should(BeNil()) + Expect(len(item4)).Should(BeEquivalentTo(1)) }) It("iterate resource", func() { @@ -1599,3 +1642,14 @@ childrenResourceType: Expect(k8sClient.Delete(context.TODO(), &cloneSetConfigMap)).Should(BeNil()) }) }) + +var cronJob = ` +apiVersion: batch/v1 +kind: CronJob +spec: + jobTemplate: + spec: + selector: + matchLabels: + app: cronJob1 +` diff --git a/vela-templates/definitions/internal/component/cron-task.cue b/vela-templates/definitions/internal/component/cron-task.cue index 3380e729c..46eac2170 100644 --- a/vela-templates/definitions/internal/component/cron-task.cue +++ b/vela-templates/definitions/internal/component/cron-task.cue @@ -47,6 +47,15 @@ template: { activeDeadlineSeconds: parameter.activeDeadlineSeconds } backoffLimit: parameter.backoffLimit + selector: { + matchLabels: { + if parameter.labels != _|_ { + parameter.labels + } + "app.oam.dev/name": context.appName + "app.oam.dev/component": context.name + } + } template: { metadata: { labels: {