diff --git a/CHANGELOG.md b/CHANGELOG.md index 531044fe5..c20939243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - (Maintenance) Move Container utils functions - (Feature) ArangoProfile Selectors - (Bugfix) Remove ImagePullSecrets Reference from Container +- (Feature) DebugPackage ArangoProfiles ## [1.2.39](https://github.com/arangodb/kube-arangodb/tree/1.2.39) (2024-03-11) - (Feature) Extract Scheduler API diff --git a/chart/kube-arangodb/templates/ml-operator/role.yaml b/chart/kube-arangodb/templates/ml-operator/role.yaml index 9c1bfc8f9..bc628afa4 100644 --- a/chart/kube-arangodb/templates/ml-operator/role.yaml +++ b/chart/kube-arangodb/templates/ml-operator/role.yaml @@ -26,6 +26,13 @@ rules: - "arangomlstorages/status" verbs: - "*" + - apiGroups: + - "scheduler.arangodb.com" + resources: + - "arangoprofiles" + - "arangoprofiles/status" + verbs: + - "*" - apiGroups: - "database.arangodb.com" resources: diff --git a/pkg/debug_package/generator.go b/pkg/debug_package/generator.go index 4cc8810b6..bb0105be6 100644 --- a/pkg/debug_package/generator.go +++ b/pkg/debug_package/generator.go @@ -44,6 +44,7 @@ var rootFactories = []shared.Factory{ kubernetes.AgencyDump(), kubernetes.ML(), kubernetes.Backup(), + kubernetes.Scheduler(), } func InitCommand(cmd *cobra.Command) { diff --git a/pkg/debug_package/generators/kubernetes/arango_backup_backup.go b/pkg/debug_package/generators/kubernetes/arango_backup_backup.go index 9a8b9135f..368bb3141 100644 --- a/pkg/debug_package/generators/kubernetes/arango_backup_backup.go +++ b/pkg/debug_package/generators/kubernetes/arango_backup_backup.go @@ -53,7 +53,7 @@ func backupBackups(logger zerolog.Logger, files chan<- shared.File, client kclie } func backupBackup(client kclient.Client, files chan<- shared.File, ext *backupApi.ArangoBackup) error { - files <- shared.NewYAMLFile(fmt.Sprintf("kubernetes/arango/backupBackup/backups/%s.yaml", ext.GetName()), func() ([]interface{}, error) { + files <- shared.NewYAMLFile(fmt.Sprintf("kubernetes/arango/backup/backups/%s.yaml", ext.GetName()), func() ([]interface{}, error) { return []interface{}{ext}, nil }) diff --git a/pkg/debug_package/generators/kubernetes/arango_scheduler.go b/pkg/debug_package/generators/kubernetes/arango_scheduler.go new file mode 100644 index 000000000..cf6dd16e9 --- /dev/null +++ b/pkg/debug_package/generators/kubernetes/arango_scheduler.go @@ -0,0 +1,47 @@ +// +// DISCLAIMER +// +// 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. +// 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 kubernetes + +import ( + "github.com/rs/zerolog" + + "github.com/arangodb/kube-arangodb/pkg/debug_package/shared" + "github.com/arangodb/kube-arangodb/pkg/util/errors" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" +) + +func Scheduler() shared.Factory { + return shared.NewFactory("scheduler", true, scheduler) +} + +func scheduler(logger zerolog.Logger, files chan<- shared.File) error { + k, ok := kclient.GetDefaultFactory().Client() + if !ok { + return errors.Errorf("Client is not initialised") + } + + if err := schedulerProfiles(logger, files, k); err != nil { + logger.Err(err).Msgf("Error while collecting arango scheduler extension") + return err + } + + return nil +} diff --git a/pkg/debug_package/generators/kubernetes/arango_scheduler_profile.go b/pkg/debug_package/generators/kubernetes/arango_scheduler_profile.go new file mode 100644 index 000000000..49b0f07bc --- /dev/null +++ b/pkg/debug_package/generators/kubernetes/arango_scheduler_profile.go @@ -0,0 +1,73 @@ +// +// 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 kubernetes + +import ( + "context" + "fmt" + + "github.com/rs/zerolog" + + schedulerApi "github.com/arangodb/kube-arangodb/pkg/apis/scheduler/v1alpha1" + "github.com/arangodb/kube-arangodb/pkg/debug_package/cli" + "github.com/arangodb/kube-arangodb/pkg/debug_package/shared" + "github.com/arangodb/kube-arangodb/pkg/util/errors" + "github.com/arangodb/kube-arangodb/pkg/util/k8sutil/kerrors" + "github.com/arangodb/kube-arangodb/pkg/util/kclient" +) + +func schedulerProfiles(logger zerolog.Logger, files chan<- shared.File, client kclient.Client) error { + profiles, err := listSchedulerProfiles(client) + if err != nil { + if kerrors.IsForbiddenOrNotFound(err) { + return nil + } + + return err + } + + if err := errors.ExecuteWithErrorArrayP2(schedulerProfile, client, files, profiles...); err != nil { + logger.Err(err).Msgf("Error while collecting arango scheduler profiles") + return err + } + + return nil +} + +func schedulerProfile(client kclient.Client, files chan<- shared.File, ext *schedulerApi.ArangoProfile) error { + files <- shared.NewYAMLFile(fmt.Sprintf("kubernetes/arango/scheduler/profiles/%s.yaml", ext.GetName()), func() ([]interface{}, error) { + return []interface{}{ext}, nil + }) + + return nil +} + +func listSchedulerProfiles(client kclient.Client) ([]*schedulerApi.ArangoProfile, error) { + return ListObjects[*schedulerApi.ArangoProfileList, *schedulerApi.ArangoProfile](context.Background(), client.Arango().SchedulerV1alpha1().ArangoProfiles(cli.GetInput().Namespace), func(result *schedulerApi.ArangoProfileList) []*schedulerApi.ArangoProfile { + q := make([]*schedulerApi.ArangoProfile, len(result.Items)) + + for id, e := range result.Items { + q[id] = e.DeepCopy() + } + + return q + }) +} diff --git a/pkg/debug_package/generators/kubernetes/lister.go b/pkg/debug_package/generators/kubernetes/lister.go index c29ac86f2..e4c486733 100644 --- a/pkg/debug_package/generators/kubernetes/lister.go +++ b/pkg/debug_package/generators/kubernetes/lister.go @@ -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. @@ -22,16 +22,95 @@ package kubernetes import ( "context" + "sort" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/k8sutil" ) +func Extract[T1, T2 any](in List[T1], ex func(in T1) T2) List[T2] { + r := make(List[T2], len(in)) + + for id := range r { + r[id] = ex(in[id]) + } + + return r +} + +type List[T any] []T + +func (l List[T]) Sort(pred func(a, b T) bool) List[T] { + sort.Slice(l, func(i, j int) bool { + return pred(l[i], l[j]) + }) + + return l +} + +func (l List[T]) Append(obj ...T) List[T] { + r := make(List[T], 0, len(l)+len(obj)) + + r = append(r, l...) + r = append(r, obj...) + + return r +} + +func (l List[T]) Filter(f func(a T) bool) List[T] { + r := make(List[T], 0, len(l)) + + for _, o := range l { + if !f(o) { + continue + } + + r = append(r, o) + } + + return r +} + +func (l List[T]) Contains(f func(a T) bool) bool { + for _, o := range l { + if f(o) { + return true + } + } + + return false +} + +func (l List[T]) Unique(f func(existing List[T], a T) bool) List[T] { + r := make(List[T], 0, len(l)) + + for _, o := range l { + if f(r, o) { + continue + } + + r = append(r, o) + } + + return r +} + type ObjectList[T meta.Object] map[types.UID]T -func (d ObjectList[T]) AsList() []T { +func (d ObjectList[T]) ByName(name string) (T, bool) { + for _, obj := range d { + if obj.GetName() == name { + return obj, true + } + } + + return util.Default[T](), false +} + +func (d ObjectList[T]) AsList() List[T] { list := make([]T, 0, len(d)) for _, p := range d { list = append(list, p)