Skip to content

Commit e7ae432

Browse files
authored
[Feature] JobScheduler Coverage (#1606)
1 parent 9459237 commit e7ae432

32 files changed

+3116
-218
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- (Bugfix) Fix Resources Copy mechanism to prevent invalid pod creation
77
- (Bugfix) Wait for ImageStatus in ImageDiscover
88
- (Bugfix) Fix Image Error Propagation
9+
- (Feature) JobScheduler Coverage
910

1011
## [1.2.38](https://github.com/arangodb/kube-arangodb/tree/1.2.38) (2024-02-22)
1112
- (Feature) Extract GRPC Server

docs/api/ArangoMLExtension.V1Alpha1.md

+155-155
Large diffs are not rendered by default.

docs/api/ArangoMLStorage.V1Alpha1.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Default Value: `9202`
116116

117117
### .spec.mode.sidecar.env
118118

119-
Type: `core.EnvVar` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/environments.go#L33)</sup>
119+
Type: `core.EnvVar` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/environments.go#L36)</sup>
120120

121121
Env keeps the information about environment variables provided to the container
122122

@@ -127,7 +127,7 @@ Links:
127127

128128
### .spec.mode.sidecar.envFrom
129129

130-
Type: `core.EnvFromSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/environments.go#L38)</sup>
130+
Type: `core.EnvFromSource` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/environments.go#L41)</sup>
131131

132132
EnvFrom keeps the information about environment variable sources provided to the container
133133

@@ -138,15 +138,15 @@ Links:
138138

139139
### .spec.mode.sidecar.image
140140

141-
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L34)</sup>
141+
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L37)</sup>
142142

143143
Image define image details
144144

145145
***
146146

147147
### .spec.mode.sidecar.imagePullPolicy
148148

149-
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L38)</sup>
149+
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L41)</sup>
150150

151151
ImagePullPolicy define Image pull policy
152152

@@ -156,7 +156,7 @@ Default Value: `IfNotPresent`
156156

157157
### .spec.mode.sidecar.imagePullSecrets
158158

159-
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L41)</sup>
159+
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/image.go#L44)</sup>
160160

161161
ImagePullSecrets define Secrets used to pull Image from registry
162162

@@ -174,7 +174,7 @@ Default Value: `9201`
174174

175175
### .spec.mode.sidecar.resources
176176

177-
Type: `core.ResourceRequirements` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/resources.go#L34)</sup>
177+
Type: `core.ResourceRequirements` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/resources.go#L37)</sup>
178178

179179
Resources holds resource requests & limits for container
180180

@@ -185,9 +185,9 @@ Links:
185185

186186
### .spec.mode.sidecar.securityContext
187187

188-
Type: `core.SecurityContext` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/security.go#L31)</sup>
188+
Type: `core.SecurityContext` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.38/pkg/apis/scheduler/v1alpha1/container/resources/security.go#L35)</sup>
189189

190-
PodSecurityContext holds pod-level security attributes and common container settings.
190+
SecurityContext holds container-level security attributes and common container settings.
191191

192192
Links:
193193
* [Kubernetes docs](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)

pkg/apis/ml/v1alpha1/storage_spec_test.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,22 @@ func Test_ArangoMLStorageSpec(t *testing.T) {
7474
expectedRequirements := core.ResourceRequirements{
7575
Requests: assignedRequirements.Requests,
7676
Limits: core.ResourceList{
77-
core.ResourceCPU: resource.MustParse("100m"),
78-
core.ResourceMemory: resource.MustParse("128Mi"),
77+
core.ResourceCPU: resource.MustParse("200m"),
78+
core.ResourceMemory: resource.MustParse("256Mi"),
7979
},
8080
}
8181

8282
actualRequirements := s.Mode.Sidecar.GetResources().With(&schedulerContainerResourcesApi.Resources{Resources: &core.ResourceRequirements{
8383
Limits: core.ResourceList{
84-
core.ResourceCPU: resource.MustParse("100m"),
85-
core.ResourceMemory: resource.MustParse("128Mi"),
86-
},
87-
Requests: core.ResourceList{
8884
core.ResourceCPU: resource.MustParse("200m"),
8985
core.ResourceMemory: resource.MustParse("256Mi"),
9086
},
87+
Requests: core.ResourceList{
88+
core.ResourceCPU: resource.MustParse("100m"),
89+
core.ResourceMemory: resource.MustParse("128Mi"),
90+
},
9191
}})
92+
9293
require.Equal(t, expectedRequirements, actualRequirements.GetResources())
9394
})
9495
}

pkg/apis/scheduler/v1alpha1/container/definition.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
core "k8s.io/api/core/v1"
2525

2626
schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container/resources"
27+
"github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/interfaces"
2728
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
2829
"github.com/arangodb/kube-arangodb/pkg/util"
30+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
2931
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/container"
3032
)
3133

@@ -89,6 +91,18 @@ func (c Containers) With(other Containers) Containers {
8991
return ret
9092
}
9193

94+
func (c Containers) Validate() error {
95+
for name, container := range c {
96+
if err := container.Validate(); err != nil {
97+
return errors.Wrapf(err, "Container %s failed", name)
98+
}
99+
}
100+
101+
return nil
102+
}
103+
104+
var _ interfaces.Container[Container] = &Container{}
105+
92106
type Container struct {
93107
// Security keeps the security settings for Container
94108
*schedulerContainerResourcesApi.Security `json:",inline"`
@@ -109,10 +123,10 @@ func (c *Container) Apply(template *core.PodTemplateSpec, container *core.Contai
109123
}
110124

111125
return shared.WithErrors(
112-
c.Security.Apply(container),
113-
c.Environments.Apply(container),
126+
c.Security.Apply(template, container),
127+
c.Environments.Apply(template, container),
114128
c.Image.Apply(template, container),
115-
c.Resources.Apply(container),
129+
c.Resources.Apply(template, container),
116130
)
117131
}
118132

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package container
22+
23+
import (
24+
"testing"
25+
26+
"github.com/stretchr/testify/require"
27+
core "k8s.io/api/core/v1"
28+
"k8s.io/apimachinery/pkg/api/resource"
29+
"k8s.io/apimachinery/pkg/util/yaml"
30+
31+
schedulerContainerResourcesApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container/resources"
32+
"github.com/arangodb/kube-arangodb/pkg/util"
33+
)
34+
35+
func applyContainer(t *testing.T, template *core.PodTemplateSpec, container *core.Container, ns ...*Container) func(in func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container)) {
36+
var i *Container
37+
38+
for _, n := range ns {
39+
require.NoError(t, n.Validate())
40+
41+
i = i.With(n)
42+
43+
require.NoError(t, i.Validate())
44+
}
45+
46+
template = template.DeepCopy()
47+
48+
if template == nil {
49+
template = &core.PodTemplateSpec{}
50+
}
51+
52+
container = container.DeepCopy()
53+
if container == nil {
54+
container = &core.Container{}
55+
}
56+
57+
template.Spec.Containers = append(template.Spec.Containers, *container)
58+
59+
container = &template.Spec.Containers[0]
60+
61+
require.NoError(t, i.Apply(template, container))
62+
63+
return func(in func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container)) {
64+
t.Run("Validate", func(t *testing.T) {
65+
if i != nil {
66+
in(t, template, container, i)
67+
} else {
68+
in(t, template, container, &Container{})
69+
}
70+
})
71+
}
72+
}
73+
74+
func applyContainerYAML(t *testing.T, template *core.PodTemplateSpec, container *core.Container, ns ...string) func(in func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container)) {
75+
elements := make([]*Container, len(ns))
76+
77+
for id := range ns {
78+
var p Container
79+
require.NoError(t, yaml.Unmarshal([]byte(ns[id]), &p))
80+
elements[id] = p.DeepCopy()
81+
}
82+
83+
return applyContainer(t, template, container, elements...)
84+
}
85+
86+
func Test_Container(t *testing.T) {
87+
t.Run("Nil", func(t *testing.T) {
88+
applyContainer(t, nil, nil)(func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container) {
89+
require.Nil(t, spec.Resources)
90+
require.Nil(t, spec.Image)
91+
require.Nil(t, spec.Security)
92+
require.Nil(t, spec.Environments)
93+
94+
require.Len(t, container.Env, 0)
95+
})
96+
})
97+
t.Run("Empty template", func(t *testing.T) {
98+
applyContainer(t, &core.PodTemplateSpec{}, &core.Container{})(func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container) {
99+
require.Nil(t, spec.Resources)
100+
require.Nil(t, spec.Image)
101+
require.Nil(t, spec.Security)
102+
require.Nil(t, spec.Environments)
103+
104+
require.Len(t, container.Env, 0)
105+
})
106+
})
107+
t.Run("With fields", func(t *testing.T) {
108+
applyContainer(t, &core.PodTemplateSpec{}, &core.Container{}, &Container{
109+
Security: &schedulerContainerResourcesApi.Security{
110+
SecurityContext: &core.SecurityContext{
111+
RunAsUser: util.NewType[int64](50),
112+
},
113+
},
114+
Environments: &schedulerContainerResourcesApi.Environments{
115+
Env: []core.EnvVar{
116+
{
117+
Name: "key1",
118+
Value: "value1",
119+
},
120+
},
121+
},
122+
Image: &schedulerContainerResourcesApi.Image{
123+
Image: util.NewType("test"),
124+
},
125+
Resources: &schedulerContainerResourcesApi.Resources{
126+
Resources: &core.ResourceRequirements{
127+
Limits: map[core.ResourceName]resource.Quantity{
128+
core.ResourceCPU: resource.MustParse("1"),
129+
},
130+
},
131+
},
132+
})(func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container) {
133+
// Spec
134+
require.NotNil(t, spec.Resources)
135+
require.NotNil(t, spec.Resources.Resources)
136+
require.Contains(t, spec.Resources.Resources.Limits, core.ResourceCPU)
137+
require.EqualValues(t, resource.MustParse("1"), spec.Resources.Resources.Limits[core.ResourceCPU])
138+
139+
require.NotNil(t, spec.Image)
140+
require.NotNil(t, spec.Image.Image)
141+
require.EqualValues(t, "test", *spec.Image.Image)
142+
143+
require.NotNil(t, spec.Security)
144+
require.NotNil(t, spec.Security.SecurityContext)
145+
require.NotNil(t, spec.Security.SecurityContext.RunAsUser)
146+
require.EqualValues(t, 50, *spec.Security.SecurityContext.RunAsUser)
147+
148+
require.NotNil(t, spec.Environments)
149+
require.Len(t, spec.Environments.Env, 1)
150+
require.EqualValues(t, "key1", spec.Environments.Env[0].Name)
151+
require.EqualValues(t, "value1", spec.Environments.Env[0].Value)
152+
})
153+
})
154+
}
155+
156+
func Test_Container_YAML(t *testing.T) {
157+
t.Run("With Override", func(t *testing.T) {
158+
applyContainerYAML(t, &core.PodTemplateSpec{}, &core.Container{}, `
159+
---
160+
securityContext:
161+
runAsUser: 50
162+
163+
env:
164+
- name: key1
165+
value: value1
166+
167+
image: test
168+
169+
resources:
170+
limits:
171+
cpu: 1
172+
`, `
173+
---
174+
175+
securityContext:
176+
runAsUser: 10
177+
`)(func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container) {
178+
// Spec
179+
require.NotNil(t, spec.Resources)
180+
require.NotNil(t, spec.Resources.Resources)
181+
require.Contains(t, spec.Resources.Resources.Limits, core.ResourceCPU)
182+
require.EqualValues(t, resource.MustParse("1"), spec.Resources.Resources.Limits[core.ResourceCPU])
183+
184+
require.NotNil(t, spec.Image)
185+
require.NotNil(t, spec.Image.Image)
186+
require.EqualValues(t, "test", *spec.Image.Image)
187+
188+
require.NotNil(t, spec.Security)
189+
require.NotNil(t, spec.Security.SecurityContext)
190+
require.NotNil(t, spec.Security.SecurityContext.RunAsUser)
191+
require.EqualValues(t, 10, *spec.Security.SecurityContext.RunAsUser)
192+
193+
require.NotNil(t, spec.Environments)
194+
require.Len(t, spec.Environments.Env, 1)
195+
require.EqualValues(t, "key1", spec.Environments.Env[0].Name)
196+
require.EqualValues(t, "value1", spec.Environments.Env[0].Value)
197+
})
198+
})
199+
t.Run("With fields", func(t *testing.T) {
200+
applyContainerYAML(t, &core.PodTemplateSpec{}, &core.Container{}, `
201+
---
202+
securityContext:
203+
runAsUser: 50
204+
205+
env:
206+
- name: key1
207+
value: value1
208+
209+
image: test
210+
211+
resources:
212+
limits:
213+
cpu: 1
214+
`)(func(t *testing.T, pod *core.PodTemplateSpec, container *core.Container, spec *Container) {
215+
// Spec
216+
require.NotNil(t, spec.Resources)
217+
require.NotNil(t, spec.Resources.Resources)
218+
require.Contains(t, spec.Resources.Resources.Limits, core.ResourceCPU)
219+
require.EqualValues(t, resource.MustParse("1"), spec.Resources.Resources.Limits[core.ResourceCPU])
220+
221+
require.NotNil(t, spec.Image)
222+
require.NotNil(t, spec.Image.Image)
223+
require.EqualValues(t, "test", *spec.Image.Image)
224+
225+
require.NotNil(t, spec.Security)
226+
require.NotNil(t, spec.Security.SecurityContext)
227+
require.NotNil(t, spec.Security.SecurityContext.RunAsUser)
228+
require.EqualValues(t, 50, *spec.Security.SecurityContext.RunAsUser)
229+
230+
require.NotNil(t, spec.Environments)
231+
require.Len(t, spec.Environments.Env, 1)
232+
require.EqualValues(t, "key1", spec.Environments.Env[0].Name)
233+
require.EqualValues(t, "value1", spec.Environments.Env[0].Value)
234+
})
235+
})
236+
}

0 commit comments

Comments
 (0)