mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
[Feature] ArangoProfile Selectors (#1627)
This commit is contained in:
parent
8be3599cd6
commit
fe5419c404
23 changed files with 808 additions and 8 deletions
|
@ -9,6 +9,7 @@
|
|||
- (Feature) Discover Namespace in DebugPackage from K8S
|
||||
- (Feature) Expose Force CRD Install option
|
||||
- (Maintenance) Move Container utils functions
|
||||
- (Feature) ArangoProfile Selectors
|
||||
|
||||
## [1.2.39](https://github.com/arangodb/kube-arangodb/tree/1.2.39) (2024-03-11)
|
||||
- (Feature) Extract Scheduler API
|
||||
|
|
|
@ -71,6 +71,14 @@ Default Value: `false`
|
|||
|
||||
***
|
||||
|
||||
### .spec.deployment.imagePullSecrets
|
||||
|
||||
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/pod/resources/image.go#L36)</sup>
|
||||
|
||||
ImagePullSecrets define Secrets used to pull Image from registry
|
||||
|
||||
***
|
||||
|
||||
### .spec.deployment.labels
|
||||
|
||||
Type: `object` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/pod/resources/metadata.go#L39)</sup>
|
||||
|
|
|
@ -8,6 +8,14 @@ title: ArangoProfile V1Alpha1
|
|||
|
||||
## Spec
|
||||
|
||||
### .spec.selectors.label
|
||||
|
||||
Type: `meta.LabelSelector` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/profile_selectors.go#L32)</sup>
|
||||
|
||||
Label keeps information about label selector
|
||||
|
||||
***
|
||||
|
||||
### .spec.template.container.all.env
|
||||
|
||||
Type: `core.EnvVar` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/container/resources/environments.go#L36)</sup>
|
||||
|
@ -281,6 +289,14 @@ Default Value: `false`
|
|||
|
||||
***
|
||||
|
||||
### .spec.template.pod.imagePullSecrets
|
||||
|
||||
Type: `array` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/pod/resources/image.go#L36)</sup>
|
||||
|
||||
ImagePullSecrets define Secrets used to pull Image from registry
|
||||
|
||||
***
|
||||
|
||||
### .spec.template.pod.labels
|
||||
|
||||
Type: `object` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.39/pkg/apis/scheduler/v1alpha1/pod/resources/metadata.go#L39)</sup>
|
||||
|
|
|
@ -42,7 +42,10 @@ func (r *Resources) Apply(_ *core.PodTemplateSpec, template *core.Container) err
|
|||
return nil
|
||||
}
|
||||
|
||||
template.Resources = r.GetResources()
|
||||
res := r.GetResources()
|
||||
|
||||
template.Resources.Limits = kresources.UpscaleContainerResourceList(template.Resources.Limits, res.Limits)
|
||||
template.Resources.Requests = kresources.UpscaleContainerResourceList(template.Resources.Requests, res.Requests)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func (v *VolumeMounts) Apply(_ *core.PodTemplateSpec, container *core.Container)
|
|||
|
||||
obj := v.DeepCopy()
|
||||
|
||||
container.VolumeMounts = obj.VolumeMounts
|
||||
container.VolumeMounts = kresources.MergeVolumeMounts(container.VolumeMounts, obj.VolumeMounts...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ type Pod struct {
|
|||
// Metadata keeps the metadata settings for Pod
|
||||
*schedulerPodResourcesApi.Metadata `json:",inline"`
|
||||
|
||||
// Image keeps the image information
|
||||
*schedulerPodResourcesApi.Image `json:",inline"`
|
||||
|
||||
// Scheduling keeps the scheduling information
|
||||
*schedulerPodResourcesApi.Scheduling `json:",inline"`
|
||||
|
||||
|
@ -65,6 +68,7 @@ func (a *Pod) With(other *Pod) *Pod {
|
|||
|
||||
return &Pod{
|
||||
Scheduling: a.Scheduling.With(other.Scheduling),
|
||||
Image: a.Image.With(other.Image),
|
||||
Namespace: a.Namespace.With(other.Namespace),
|
||||
Security: a.Security.With(other.Security),
|
||||
Volumes: a.Volumes.With(other.Volumes),
|
||||
|
@ -80,6 +84,7 @@ func (a *Pod) Apply(template *core.PodTemplateSpec) error {
|
|||
|
||||
return shared.WithErrors(
|
||||
a.Scheduling.Apply(template),
|
||||
a.Image.Apply(template),
|
||||
a.Namespace.Apply(template),
|
||||
a.Security.Apply(template),
|
||||
a.Volumes.Apply(template),
|
||||
|
@ -96,6 +101,14 @@ func (a *Pod) GetSecurity() *schedulerPodResourcesApi.Security {
|
|||
return a.Security
|
||||
}
|
||||
|
||||
func (a *Pod) GetImage() *schedulerPodResourcesApi.Image {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return a.Image
|
||||
}
|
||||
|
||||
func (a *Pod) GetScheduling() *schedulerPodResourcesApi.Scheduling {
|
||||
if a == nil {
|
||||
return nil
|
||||
|
@ -142,6 +155,7 @@ func (a *Pod) Validate() error {
|
|||
}
|
||||
return shared.WithErrors(
|
||||
a.Scheduling.Validate(),
|
||||
a.Image.Validate(),
|
||||
a.Namespace.Validate(),
|
||||
a.Security.Validate(),
|
||||
a.Volumes.Validate(),
|
||||
|
|
87
pkg/apis/scheduler/v1alpha1/pod/resources/image.go
Normal file
87
pkg/apis/scheduler/v1alpha1/pod/resources/image.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/interfaces"
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
)
|
||||
|
||||
type ImagePullSecrets []string
|
||||
|
||||
var _ interfaces.Pod[Image] = &Image{}
|
||||
|
||||
type Image struct {
|
||||
// ImagePullSecrets define Secrets used to pull Image from registry
|
||||
ImagePullSecrets ImagePullSecrets `json:"imagePullSecrets,omitempty"`
|
||||
}
|
||||
|
||||
func (i *Image) Apply(pod *core.PodTemplateSpec) error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, secret := range i.ImagePullSecrets {
|
||||
if hasImagePullSecret(pod.Spec.ImagePullSecrets, secret) {
|
||||
continue
|
||||
}
|
||||
|
||||
pod.Spec.ImagePullSecrets = append(pod.Spec.ImagePullSecrets, core.LocalObjectReference{
|
||||
Name: secret,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) With(other *Image) *Image {
|
||||
if i == nil && other == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if other == nil {
|
||||
return i.DeepCopy()
|
||||
}
|
||||
|
||||
return other.DeepCopy()
|
||||
}
|
||||
|
||||
func (i *Image) Validate() error {
|
||||
if i == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceErrors("pullSecrets", shared.ValidateList(i.ImagePullSecrets, shared.ValidateResourceName)),
|
||||
)
|
||||
}
|
||||
|
||||
func hasImagePullSecret(secrets []core.LocalObjectReference, secret string) bool {
|
||||
for _, sec := range secrets {
|
||||
if sec.Name == secret {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
88
pkg/apis/scheduler/v1alpha1/pod/resources/image_test.go
Normal file
88
pkg/apis/scheduler/v1alpha1/pod/resources/image_test.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
core "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func applyImage(t *testing.T, template *core.PodTemplateSpec, ns ...*Image) func(in func(t *testing.T, pod *core.PodTemplateSpec)) {
|
||||
var i *Image
|
||||
|
||||
for _, n := range ns {
|
||||
require.NoError(t, n.Validate())
|
||||
|
||||
i = i.With(n)
|
||||
|
||||
require.NoError(t, i.Validate())
|
||||
}
|
||||
|
||||
template = template.DeepCopy()
|
||||
|
||||
if template == nil {
|
||||
template = &core.PodTemplateSpec{}
|
||||
}
|
||||
|
||||
require.NoError(t, i.Apply(template))
|
||||
|
||||
return func(in func(t *testing.T, spec *core.PodTemplateSpec)) {
|
||||
t.Run("Validate", func(t *testing.T) {
|
||||
in(t, template)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Image(t *testing.T) {
|
||||
t.Run("With Nil", func(t *testing.T) {
|
||||
applyImage(t, nil, nil)(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Spec.ImagePullSecrets, 0)
|
||||
})
|
||||
})
|
||||
t.Run("With Empty", func(t *testing.T) {
|
||||
applyImage(t, &core.PodTemplateSpec{})(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Spec.ImagePullSecrets, 0)
|
||||
})
|
||||
})
|
||||
t.Run("With PS", func(t *testing.T) {
|
||||
applyImage(t, &core.PodTemplateSpec{}, &Image{
|
||||
ImagePullSecrets: []string{
|
||||
"secret",
|
||||
},
|
||||
})(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Spec.ImagePullSecrets, 1)
|
||||
require.Equal(t, "secret", pod.Spec.ImagePullSecrets[0].Name)
|
||||
})
|
||||
})
|
||||
t.Run("With PS2", func(t *testing.T) {
|
||||
applyImage(t, &core.PodTemplateSpec{}, &Image{
|
||||
ImagePullSecrets: []string{
|
||||
"secret",
|
||||
"secret",
|
||||
},
|
||||
})(func(t *testing.T, pod *core.PodTemplateSpec) {
|
||||
require.Len(t, pod.Spec.ImagePullSecrets, 1)
|
||||
require.Equal(t, "secret", pod.Spec.ImagePullSecrets[0].Name)
|
||||
})
|
||||
})
|
||||
}
|
|
@ -46,7 +46,9 @@ func (s *ServiceAccount) Apply(template *core.PodTemplateSpec) error {
|
|||
c := s.DeepCopy()
|
||||
|
||||
template.Spec.ServiceAccountName = c.ServiceAccountName
|
||||
template.Spec.AutomountServiceAccountToken = c.AutomountServiceAccountToken
|
||||
if c.AutomountServiceAccountToken != nil {
|
||||
template.Spec.AutomountServiceAccountToken = c.AutomountServiceAccountToken
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func (v *Volumes) Apply(template *core.PodTemplateSpec) error {
|
|||
|
||||
obj := v.DeepCopy()
|
||||
|
||||
template.Spec.Volumes = obj.Volumes
|
||||
template.Spec.Volumes = kresources.MergeVolumes(template.Spec.Volumes, obj.Volumes...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -30,6 +30,47 @@ import (
|
|||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Image) DeepCopyInto(out *Image) {
|
||||
*out = *in
|
||||
if in.ImagePullSecrets != nil {
|
||||
in, out := &in.ImagePullSecrets, &out.ImagePullSecrets
|
||||
*out = make(ImagePullSecrets, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Image.
|
||||
func (in *Image) DeepCopy() *Image {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Image)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ImagePullSecrets) DeepCopyInto(out *ImagePullSecrets) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ImagePullSecrets, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImagePullSecrets.
|
||||
func (in ImagePullSecrets) DeepCopy() ImagePullSecrets {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImagePullSecrets)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Metadata) DeepCopyInto(out *Metadata) {
|
||||
*out = *in
|
||||
|
|
|
@ -37,6 +37,11 @@ func (in *Pod) DeepCopyInto(out *Pod) {
|
|||
*out = new(resources.Metadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Image != nil {
|
||||
in, out := &in.Image, &out.Image
|
||||
*out = new(resources.Image)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Scheduling != nil {
|
||||
in, out := &in.Scheduling, &out.Scheduling
|
||||
*out = new(resources.Scheduling)
|
||||
|
|
49
pkg/apis/scheduler/v1alpha1/profile_selectors.go
Normal file
49
pkg/apis/scheduler/v1alpha1/profile_selectors.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
kresources "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources"
|
||||
)
|
||||
|
||||
type ProfileSelectors struct {
|
||||
// Label keeps information about label selector
|
||||
// +doc/type: meta.LabelSelector
|
||||
Label *meta.LabelSelector `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
func (p *ProfileSelectors) Validate() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProfileSelectors) Select(labels map[string]string) bool {
|
||||
if p == nil || p.Label == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return kresources.SelectLabels(p.Label, labels)
|
||||
}
|
|
@ -20,7 +20,25 @@
|
|||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
shared "github.com/arangodb/kube-arangodb/pkg/apis/shared"
|
||||
)
|
||||
|
||||
type ProfileSpec struct {
|
||||
// Selectors keeps information about ProfileSelectors
|
||||
Selectors *ProfileSelectors `json:"selectors,omitempty"`
|
||||
|
||||
// Template keeps the Profile Template
|
||||
Template *ProfileTemplate `json:"template,omitempty"`
|
||||
}
|
||||
|
||||
func (p *ProfileSpec) Validate() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return shared.WithErrors(
|
||||
shared.PrefixResourceErrors("selectors", p.Selectors.Validate()),
|
||||
shared.PrefixResourceErrors("template", p.Template.Validate()),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ type ProfileTemplates []*ProfileTemplate
|
|||
func (p ProfileTemplates) Sort() ProfileTemplates {
|
||||
sort.Slice(p, func(i, j int) bool {
|
||||
if a, b := p[i].GetPriority(), p[j].GetPriority(); a != b {
|
||||
return a < b
|
||||
return a > b
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -45,8 +45,8 @@ func (p ProfileTemplates) Sort() ProfileTemplates {
|
|||
func (p ProfileTemplates) Merge() *ProfileTemplate {
|
||||
var z *ProfileTemplate
|
||||
|
||||
for _, q := range p {
|
||||
z = z.With(q)
|
||||
for id := len(p) - 1; id >= 0; id-- {
|
||||
z = z.With(p[id])
|
||||
}
|
||||
|
||||
return z
|
||||
|
|
27
pkg/apis/scheduler/v1alpha1/zz_generated.deepcopy.go
generated
27
pkg/apis/scheduler/v1alpha1/zz_generated.deepcopy.go
generated
|
@ -28,6 +28,7 @@ package v1alpha1
|
|||
import (
|
||||
container "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/container"
|
||||
pod "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1/pod"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
|
@ -120,9 +121,35 @@ func (in *ProfileContainerTemplate) DeepCopy() *ProfileContainerTemplate {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ProfileSelectors) DeepCopyInto(out *ProfileSelectors) {
|
||||
*out = *in
|
||||
if in.Label != nil {
|
||||
in, out := &in.Label, &out.Label
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProfileSelectors.
|
||||
func (in *ProfileSelectors) DeepCopy() *ProfileSelectors {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ProfileSelectors)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ProfileSpec) DeepCopyInto(out *ProfileSpec) {
|
||||
*out = *in
|
||||
if in.Selectors != nil {
|
||||
in, out := &in.Selectors, &out.Selectors
|
||||
*out = new(ProfileSelectors)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Template != nil {
|
||||
in, out := &in.Template, &out.Template
|
||||
*out = new(ProfileTemplate)
|
||||
|
|
|
@ -349,6 +349,10 @@ v1alpha1:
|
|||
type: boolean
|
||||
hostPID:
|
||||
type: boolean
|
||||
imagePullSecrets:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
|
@ -3,6 +3,31 @@ v1alpha1:
|
|||
properties:
|
||||
spec:
|
||||
properties:
|
||||
selectors:
|
||||
description: Selectors keeps information about ProfileSelectors
|
||||
properties:
|
||||
label:
|
||||
description: Label keeps information about label selector
|
||||
properties:
|
||||
matchExpressions:
|
||||
items:
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
operator:
|
||||
type: string
|
||||
values:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
template:
|
||||
description: Template keeps the Profile Template
|
||||
properties:
|
||||
|
@ -930,6 +955,10 @@ v1alpha1:
|
|||
type: boolean
|
||||
hostPID:
|
||||
type: boolean
|
||||
imagePullSecrets:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
labels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
|
76
pkg/util/k8sutil/resources/selectors.go
Normal file
76
pkg/util/k8sutil/resources/selectors.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/strings"
|
||||
)
|
||||
|
||||
func SelectLabels(selector *meta.LabelSelector, labels map[string]string) bool {
|
||||
if selector == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, v := range selector.MatchLabels {
|
||||
if v2, ok := labels[k]; !ok || v2 != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, req := range selector.MatchExpressions {
|
||||
switch req.Operator {
|
||||
case meta.LabelSelectorOpIn:
|
||||
if len(req.Values) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := labels[req.Key]; !ok {
|
||||
return false
|
||||
} else if !strings.ListContains(req.Values, v) {
|
||||
return false
|
||||
}
|
||||
case meta.LabelSelectorOpNotIn:
|
||||
if len(req.Values) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := labels[req.Key]; ok {
|
||||
if strings.ListContains(req.Values, v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case meta.LabelSelectorOpExists:
|
||||
if _, ok := labels[req.Key]; !ok {
|
||||
return false
|
||||
}
|
||||
case meta.LabelSelectorOpDoesNotExist:
|
||||
if _, ok := labels[req.Key]; ok {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
249
pkg/util/k8sutil/resources/selectors_test.go
Normal file
249
pkg/util/k8sutil/resources/selectors_test.go
Normal file
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package resources
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Test_Selectors_Labels(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"A": "B",
|
||||
"C": "D",
|
||||
"E": "F",
|
||||
}
|
||||
|
||||
t.Run("Match", func(t *testing.T) {
|
||||
t.Run("Do not match with nil", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(nil, nil))
|
||||
})
|
||||
t.Run("Match with any", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{}, nil))
|
||||
})
|
||||
t.Run("Match with dedicated labels select", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"A": "B",
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Match with multiple dedicated labels select", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"A": "B",
|
||||
"E": "F",
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Match with mismatch dedicated labels select", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"A": "B",
|
||||
"E": "G",
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Match Expression", func(t *testing.T) {
|
||||
t.Run("Exists", func(t *testing.T) {
|
||||
t.Run("Present", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Missing", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "B",
|
||||
Operator: meta.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Exists", func(t *testing.T) {
|
||||
t.Run("Present", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Missing", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "B",
|
||||
Operator: meta.LabelSelectorOpDoesNotExist,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("In", func(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpIn,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Present", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpIn,
|
||||
Values: []string{
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Present Multiple", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpIn,
|
||||
Values: []string{
|
||||
"E",
|
||||
"Z",
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Missing", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "B",
|
||||
Operator: meta.LabelSelectorOpIn,
|
||||
Values: []string{
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("NotIn", func(t *testing.T) {
|
||||
t.Run("Not Existing", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "Z",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Present", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
Values: []string{
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Present Multiple", func(t *testing.T) {
|
||||
require.False(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
Values: []string{
|
||||
"E",
|
||||
"Z",
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Missing", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "B",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
Values: []string{
|
||||
"B",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
t.Run("Missing Value", func(t *testing.T) {
|
||||
require.True(t, SelectLabels(&meta.LabelSelector{
|
||||
MatchExpressions: []meta.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "A",
|
||||
Operator: meta.LabelSelectorOpNotIn,
|
||||
Values: []string{
|
||||
"R",
|
||||
"Z",
|
||||
"D",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, labels))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
|
@ -41,6 +41,8 @@ import (
|
|||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/ml"
|
||||
mlApi "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/apis/scheduler"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1"
|
||||
arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
|
||||
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
|
||||
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
|
||||
|
@ -205,6 +207,12 @@ func CreateObjects(t *testing.T, k8s kubernetes.Interface, arango arangoClientSe
|
|||
vl := *v
|
||||
_, err := arango.MlV1alpha1().ArangoMLCronJobs(vl.GetNamespace()).Create(context.Background(), vl, meta.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
case **schedulerApi.ArangoProfile:
|
||||
require.NotNil(t, v)
|
||||
|
||||
vl := *v
|
||||
_, err := arango.SchedulerV1alpha1().ArangoProfiles(vl.GetNamespace()).Create(context.Background(), vl, meta.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
require.Fail(t, fmt.Sprintf("Unable to create object: %s", reflect.TypeOf(v).String()))
|
||||
}
|
||||
|
@ -325,6 +333,12 @@ func UpdateObjects(t *testing.T, k8s kubernetes.Interface, arango arangoClientSe
|
|||
vl := *v
|
||||
_, err := k8s.RbacV1().RoleBindings(vl.GetNamespace()).Update(context.Background(), vl, meta.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
case **schedulerApi.ArangoProfile:
|
||||
require.NotNil(t, v)
|
||||
|
||||
vl := *v
|
||||
_, err := arango.SchedulerV1alpha1().ArangoProfiles(vl.GetNamespace()).Update(context.Background(), vl, meta.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
default:
|
||||
require.Fail(t, fmt.Sprintf("Unable to create object: %s", reflect.TypeOf(v).String()))
|
||||
}
|
||||
|
@ -700,6 +714,21 @@ func RefreshObjects(t *testing.T, k8s kubernetes.Interface, arango arangoClientS
|
|||
} else {
|
||||
*v = vn
|
||||
}
|
||||
case **schedulerApi.ArangoProfile:
|
||||
require.NotNil(t, v)
|
||||
|
||||
vl := *v
|
||||
|
||||
vn, err := arango.SchedulerV1alpha1().ArangoProfiles(vl.GetNamespace()).Get(context.Background(), vl.GetName(), meta.GetOptions{})
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
*v = nil
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
} else {
|
||||
*v = vn
|
||||
}
|
||||
default:
|
||||
require.Fail(t, fmt.Sprintf("Unable to get object: %s", reflect.TypeOf(v).String()))
|
||||
}
|
||||
|
@ -832,11 +861,23 @@ func SetMetaBasedOnType(t *testing.T, object meta.Object) {
|
|||
ml.ArangoMLCronJobResourcePlural,
|
||||
object.GetNamespace(),
|
||||
object.GetName()))
|
||||
case *schedulerApi.ArangoProfile:
|
||||
v.Kind = scheduler.ArangoProfileResourceKind
|
||||
v.APIVersion = schedulerApi.SchemeGroupVersion.String()
|
||||
v.SetSelfLink(fmt.Sprintf("/api/%s/%s/%s/%s",
|
||||
schedulerApi.SchemeGroupVersion.String(),
|
||||
scheduler.ArangoProfileResourcePlural,
|
||||
object.GetNamespace(),
|
||||
object.GetName()))
|
||||
default:
|
||||
require.Fail(t, fmt.Sprintf("Unable to create object: %s", reflect.TypeOf(v).String()))
|
||||
}
|
||||
}
|
||||
|
||||
func NewMetaObjectInDefaultNamespace[T meta.Object](t *testing.T, name string, mods ...MetaObjectMod[T]) T {
|
||||
return NewMetaObject[T](t, FakeNamespace, name, mods...)
|
||||
}
|
||||
|
||||
func NewMetaObject[T meta.Object](t *testing.T, namespace, name string, mods ...MetaObjectMod[T]) T {
|
||||
var obj T
|
||||
|
||||
|
@ -951,6 +992,10 @@ func NewItem(t *testing.T, o operation.Operation, object meta.Object) operation.
|
|||
item.Group = ml.ArangoMLGroupName
|
||||
item.Version = mlApi.ArangoMLVersion
|
||||
item.Kind = ml.ArangoMLCronJobResourceKind
|
||||
case *schedulerApi.ArangoProfile:
|
||||
item.Group = scheduler.ArangoSchedulerGroupName
|
||||
item.Version = schedulerApi.ArangoSchedulerVersion
|
||||
item.Kind = scheduler.ArangoProfileResourceKind
|
||||
default:
|
||||
require.Fail(t, fmt.Sprintf("Unable to create object: %s", reflect.TypeOf(v).String()))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2023 ArangoDB GmbH, Cologne, Germany
|
||||
// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -34,6 +34,7 @@ import (
|
|||
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
|
||||
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
|
||||
mlApi "github.com/arangodb/kube-arangodb/pkg/apis/ml/v1alpha1"
|
||||
schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1"
|
||||
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
|
||||
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
|
||||
)
|
||||
|
@ -76,4 +77,5 @@ func Test_NewMetaObject(t *testing.T) {
|
|||
NewMetaObjectRun[*backupApi.ArangoBackup](t)
|
||||
NewMetaObjectRun[*mlApi.ArangoMLExtension](t)
|
||||
NewMetaObjectRun[*mlApi.ArangoMLStorage](t)
|
||||
NewMetaObjectRun[*schedulerApi.ArangoProfile](t)
|
||||
}
|
||||
|
|
36
pkg/util/tests/resources.go
Normal file
36
pkg/util/tests/resources.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
core "k8s.io/api/core/v1"
|
||||
|
||||
kresources "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/resources"
|
||||
)
|
||||
|
||||
func GetContainerByNameT(t *testing.T, containers []core.Container, name string) core.Container {
|
||||
id := kresources.GetContainerIDByName(containers, name)
|
||||
require.NotEqualValues(t, -1, id)
|
||||
return containers[id]
|
||||
}
|
Loading…
Reference in a new issue