1
0
Fork 0
mirror of https://github.com/arangodb/kube-arangodb.git synced 2024-12-14 11:57:37 +00:00

[Feature] [Integration] SchedulerV2 (#1744)

This commit is contained in:
Adam Janikowski 2024-10-18 17:19:57 +02:00 committed by GitHub
parent ac0a256735
commit 846b6218af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1169 additions and 3 deletions

View file

@ -6,6 +6,7 @@
- (Feature) Helm Client Extension
- (Feature) (Integration) SchedulerV2 Definition
- (Maintenance) Proto Lint
- (Feature) (Integration) SchedulerV2
## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14)
- (Feature) ArangoRoute CRD

View file

@ -0,0 +1,59 @@
//
// 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 v2
import "github.com/pkg/errors"
type Mod func(c Configuration) Configuration
func NewConfiguration() Configuration {
return Configuration{
Namespace: "default",
}
}
type Configuration struct {
Namespace string
Deployment string
}
func (c Configuration) Validate() error {
if c.Deployment == "" {
return errors.Errorf("Invalid empty name of deployment")
}
if c.Namespace == "" {
return errors.Errorf("Invalid empty name of namespace")
}
return nil
}
func (c Configuration) With(mods ...Mod) Configuration {
n := c
for _, mod := range mods {
n = mod(n)
}
return n
}

View file

@ -0,0 +1,25 @@
//
// 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 v2
const (
LabelArangoDBDeploymentName = "deployment.arangodb.com/name"
)

View file

@ -0,0 +1,222 @@
//
// 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 v2
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"helm.sh/helm/v3/pkg/action"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition"
"github.com/arangodb/kube-arangodb/pkg/util"
)
func (i *implementation) Alive(ctx context.Context, in *pbSharedV1.Empty) (*pbSharedV1.Empty, error) {
if err := i.client.Alive(ctx); err != nil {
logger.Err(err).Warn("Helm is not alive")
return nil, status.Errorf(codes.Unavailable, "Service is not alive")
}
return &pbSharedV1.Empty{}, nil
}
func (i *implementation) List(ctx context.Context, in *pbSchedulerV2.SchedulerV2ListRequest) (*pbSchedulerV2.SchedulerV2ListResponse, error) {
var mods []util.Mod[action.List]
mods = append(mods, in.GetOptions().Options()...)
mods = append(mods, func(action *action.List) {
var s = labels.NewSelector()
if action.Selector != "" {
if n, err := labels.Parse(action.Selector); err == nil {
s = n
}
}
if r, err := labels.NewRequirement(LabelArangoDBDeploymentName, selection.DoubleEquals, []string{i.cfg.Deployment}); err != nil {
logger.Err(err).Warn("Unable to render selector")
} else if r != nil {
s = s.Add(*r)
}
action.Selector = s.String()
})
resp, err := i.client.List(ctx, mods...)
if err != nil {
logger.Err(err).Warn("Unable to run action: List")
return nil, status.Errorf(codes.Internal, "Unable to run action: List: %s", err.Error())
}
releases := make(map[string]*pbSchedulerV2.SchedulerV2Release, len(resp))
for _, r := range resp {
releases[r.Name] = newChartReleaseFromHelmRelease(&r)
}
return &pbSchedulerV2.SchedulerV2ListResponse{
Releases: releases,
}, nil
}
func (i *implementation) Status(ctx context.Context, in *pbSchedulerV2.SchedulerV2StatusRequest) (*pbSchedulerV2.SchedulerV2StatusResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
resp, err := i.client.Status(ctx, in.GetName())
if err != nil {
logger.Err(err).Warn("Unable to run action: Status")
return nil, status.Errorf(codes.Internal, "Unable to run action: Status: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2StatusResponse{
Release: newChartReleaseFromHelmRelease(resp),
}, nil
}
func (i *implementation) StatusObjects(ctx context.Context, in *pbSchedulerV2.SchedulerV2StatusObjectsRequest) (*pbSchedulerV2.SchedulerV2StatusObjectsResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
resp, objs, err := i.client.StatusObjects(ctx, in.GetName())
if err != nil {
logger.Err(err).Warn("Unable to run action: Status")
return nil, status.Errorf(codes.Internal, "Unable to run action: Status: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2StatusObjectsResponse{
Release: newChartReleaseFromHelmRelease(resp),
Objects: newReleaseInfoResourceObjectsFromResourceObjects(objs),
}, nil
}
func (i *implementation) Install(ctx context.Context, in *pbSchedulerV2.SchedulerV2InstallRequest) (*pbSchedulerV2.SchedulerV2InstallResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
var mods []util.Mod[action.Install]
mods = append(mods, in.GetOptions().Options()...)
mods = append(mods, func(action *action.Install) {
action.ReleaseName = in.GetName()
action.Namespace = i.cfg.Namespace
if action.Labels == nil {
action.Labels = map[string]string{}
}
action.Labels[LabelArangoDBDeploymentName] = i.cfg.Deployment
})
resp, err := i.client.Install(ctx, in.GetChart(), in.GetValues(), mods...)
if err != nil {
logger.Err(err).Warn("Unable to run action: Install")
return nil, status.Errorf(codes.Internal, "Unable to run action: Install: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2InstallResponse{
Release: newChartReleaseFromHelmRelease(resp),
}, nil
}
func (i *implementation) Upgrade(ctx context.Context, in *pbSchedulerV2.SchedulerV2UpgradeRequest) (*pbSchedulerV2.SchedulerV2UpgradeResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
var mods []util.Mod[action.Upgrade]
mods = append(mods, in.GetOptions().Options()...)
mods = append(mods, func(action *action.Upgrade) {
action.Namespace = i.cfg.Namespace
if action.Labels == nil {
action.Labels = map[string]string{}
}
action.Labels[LabelArangoDBDeploymentName] = i.cfg.Deployment
})
resp, err := i.client.Upgrade(ctx, in.GetName(), in.GetChart(), in.GetValues(), mods...)
if err != nil {
logger.Err(err).Warn("Unable to run action: Upgrade")
return nil, status.Errorf(codes.Internal, "Unable to run action: Upgrade: %s", err.Error())
}
var r pbSchedulerV2.SchedulerV2UpgradeResponse
if q := resp.Before; q != nil {
r.Before = newChartReleaseFromHelmRelease(q)
}
if q := resp.After; q != nil {
r.After = newChartReleaseFromHelmRelease(q)
}
return &r, nil
}
func (i *implementation) Uninstall(ctx context.Context, in *pbSchedulerV2.SchedulerV2UninstallRequest) (*pbSchedulerV2.SchedulerV2UninstallResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
var mods []util.Mod[action.Uninstall]
mods = append(mods, in.GetOptions().Options()...)
resp, err := i.client.Uninstall(ctx, in.GetName(), mods...)
if err != nil {
logger.Err(err).Warn("Unable to run action: Uninstall")
return nil, status.Errorf(codes.Internal, "Unable to run action: Uninstall: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2UninstallResponse{
Info: resp.Info,
Release: newChartReleaseFromHelmRelease(&resp.Release),
}, nil
}
func (i *implementation) Test(ctx context.Context, in *pbSchedulerV2.SchedulerV2TestRequest) (*pbSchedulerV2.SchedulerV2TestResponse, error) {
if in.GetName() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Name cannot be empty")
}
var mods []util.Mod[action.ReleaseTesting]
mods = append(mods, in.GetOptions().Options()...)
resp, err := i.client.Test(ctx, in.GetName(), mods...)
if err != nil {
logger.Err(err).Warn("Unable to run action: Test")
return nil, status.Errorf(codes.Internal, "Unable to run action: Test: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2TestResponse{
Release: newChartReleaseFromHelmRelease(resp),
}, nil
}

View 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 v2
import (
"context"
"google.golang.org/grpc"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
)
var _ pbSchedulerV2.SchedulerV2Server = &implementation{}
var _ svc.Handler = &implementation{}
func New(client helm.Client, cfg Configuration) (svc.Handler, error) {
return newInternal(client, cfg)
}
func newInternal(client helm.Client, c Configuration) (*implementation, error) {
if err := c.Validate(); err != nil {
return nil, err
}
return &implementation{
cfg: c,
client: client,
}, nil
}
type implementation struct {
cfg Configuration
client helm.Client
pbSchedulerV2.UnimplementedSchedulerV2Server
}
func (i *implementation) Name() string {
return pbSchedulerV2.Name
}
func (i *implementation) Register(registrar *grpc.Server) {
pbSchedulerV2.RegisterSchedulerV2Server(registrar, i)
}
func (i *implementation) Health() svc.HealthState {
return svc.Healthy
}
func (i *implementation) InvalidateCache(ctx context.Context, in *pbSharedV1.Empty) (*pbSharedV1.Empty, error) {
i.client.Invalidate()
return &pbSharedV1.Empty{}, nil
}

View file

@ -0,0 +1,277 @@
//
// 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 v2
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"helm.sh/helm/v3/pkg/action"
"github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
"github.com/arangodb/kube-arangodb/pkg/util/tests"
)
func cleanup(t *testing.T, c helm.Client) func() {
t.Run("Cleanup Pre", func(t *testing.T) {
items, err := c.List(context.Background(), func(in *action.List) {
in.All = true
in.StateMask = action.ListDeployed | action.ListUninstalled | action.ListUninstalling | action.ListPendingInstall | action.ListPendingUpgrade | action.ListPendingRollback | action.ListSuperseded | action.ListFailed | action.ListUnknown
})
require.NoError(t, err)
for _, item := range items {
t.Run(item.Name, func(t *testing.T) {
_, err := c.Uninstall(context.Background(), item.Name)
require.NoError(t, err)
})
}
})
return func() {
t.Run("Cleanup Post", func(t *testing.T) {
items, err := c.List(context.Background(), func(in *action.List) {
in.All = true
in.StateMask = action.ListDeployed | action.ListUninstalled | action.ListUninstalling | action.ListPendingInstall | action.ListPendingUpgrade | action.ListPendingRollback | action.ListSuperseded | action.ListFailed | action.ListUnknown
})
require.NoError(t, err)
for _, item := range items {
t.Run(item.Name, func(t *testing.T) {
_, err := c.Uninstall(context.Background(), item.Name)
require.NoError(t, err)
})
}
})
}
}
func Test_Implementation(t *testing.T) {
ctx, c := context.WithCancel(context.Background())
defer c()
scheduler, h := Client(t, ctx, func(c Configuration) Configuration {
c.Namespace = tests.FakeNamespace
c.Deployment = tests.FakeNamespace
return c
})
values, err := helm.NewValues(map[string]string{
"A": "B",
})
require.NoError(t, err)
defer cleanup(t, h)()
t.Run("Alive", func(t *testing.T) {
_, err := scheduler.Alive(context.Background(), &pbSharedV1.Empty{})
require.NoError(t, err)
})
t.Run("Check API Resources", func(t *testing.T) {
o, err := scheduler.DiscoverAPIResources(context.Background(), &definition.SchedulerV2DiscoverAPIResourcesRequest{
Version: "v1",
})
require.NoError(t, err)
for _, z := range o.Resources {
t.Logf("Kind: %s", z.GetKind())
}
})
t.Run("Check API Resource", func(t *testing.T) {
oz, err := scheduler.DiscoverAPIResource(context.Background(), &definition.SchedulerV2DiscoverAPIResourceRequest{
Version: "v1",
Kind: "ConfigMap",
})
require.NoError(t, err)
require.NotNil(t, oz.GetResource())
})
t.Run("Check API Resource - Missing", func(t *testing.T) {
oz, err := scheduler.DiscoverAPIResource(context.Background(), &definition.SchedulerV2DiscoverAPIResourceRequest{
Version: "v1",
Kind: "ConfigMap2",
})
require.NoError(t, err)
require.Nil(t, oz.GetResource())
})
t.Run("Status on Missing", func(t *testing.T) {
status, err := scheduler.Status(context.Background(), &definition.SchedulerV2StatusRequest{
Name: "test",
})
require.NoError(t, err)
require.Nil(t, status.GetRelease())
})
t.Run("List Empty", func(t *testing.T) {
status, err := scheduler.List(context.Background(), &definition.SchedulerV2ListRequest{})
require.NoError(t, err)
require.Len(t, status.GetReleases(), 0)
})
t.Run("Install", func(t *testing.T) {
status, err := scheduler.Install(context.Background(), &definition.SchedulerV2InstallRequest{
Name: "test",
Values: nil,
Chart: example_1_0_0,
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
})
t.Run("List After", func(t *testing.T) {
status, err := scheduler.List(context.Background(), &definition.SchedulerV2ListRequest{})
require.NoError(t, err)
require.Len(t, status.GetReleases(), 1)
})
t.Run("Install Outside", func(t *testing.T) {
resp, err := h.Install(context.Background(), example_1_0_0, nil, func(in *action.Install) {
in.ReleaseName = "test-outside"
})
require.NoError(t, err)
require.NotNil(t, resp)
})
t.Run("List After - Still should not see first one", func(t *testing.T) {
status, err := scheduler.List(context.Background(), &definition.SchedulerV2ListRequest{})
require.NoError(t, err)
require.Len(t, status.GetReleases(), 1)
})
t.Run("Install Second", func(t *testing.T) {
status, err := scheduler.Install(context.Background(), &definition.SchedulerV2InstallRequest{
Name: "test-x",
Values: nil,
Chart: example_1_0_0,
Options: &definition.SchedulerV2InstallRequestOptions{
Labels: map[string]string{
"X": "X",
},
},
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
})
t.Run("Install Second Outside", func(t *testing.T) {
resp, err := h.Install(context.Background(), example_1_0_0, nil, func(in *action.Install) {
in.ReleaseName = "test-outside-x"
in.Labels = map[string]string{
"X": "X",
}
})
require.NoError(t, err)
require.NotNil(t, resp)
})
t.Run("List After - Should see 2 services", func(t *testing.T) {
status, err := scheduler.List(context.Background(), &definition.SchedulerV2ListRequest{})
require.NoError(t, err)
require.Len(t, status.GetReleases(), 2)
})
t.Run("List After - Filter one", func(t *testing.T) {
status, err := scheduler.List(context.Background(), &definition.SchedulerV2ListRequest{
Options: &definition.SchedulerV2ListRequestOptions{
Selectors: map[string]string{
"X": "X",
},
},
})
require.NoError(t, err)
require.Len(t, status.GetReleases(), 1)
})
t.Run("Check - Version 1", func(t *testing.T) {
status, err := scheduler.Status(context.Background(), &definition.SchedulerV2StatusRequest{
Name: "test",
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
require.EqualValues(t, 1, status.GetRelease().GetVersion())
t.Logf("Data: %s", string(status.GetRelease().GetValues()))
require.Len(t, status.GetRelease().GetValues(), 4)
})
t.Run("Upgrade", func(t *testing.T) {
status, err := scheduler.Upgrade(context.Background(), &definition.SchedulerV2UpgradeRequest{
Name: "test",
Values: values,
Chart: example_1_0_0,
})
require.NoError(t, err)
require.NotNil(t, status.GetAfter())
t.Logf("Data: %s", string(status.GetAfter().GetValues()))
require.Len(t, status.GetAfter().GetValues(), len(values))
})
t.Run("Check - Version 2", func(t *testing.T) {
status, err := scheduler.Status(context.Background(), &definition.SchedulerV2StatusRequest{
Name: "test",
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
require.EqualValues(t, 2, status.GetRelease().GetVersion())
t.Logf("Data: %s", string(status.GetRelease().GetValues()))
require.Len(t, status.GetRelease().GetValues(), len(values))
})
t.Run("Test", func(t *testing.T) {
status, err := scheduler.Test(context.Background(), &definition.SchedulerV2TestRequest{
Name: "test",
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
})
t.Run("Uninstall", func(t *testing.T) {
status, err := scheduler.Uninstall(context.Background(), &definition.SchedulerV2UninstallRequest{
Name: "test",
})
require.NoError(t, err)
require.NotNil(t, status.GetRelease())
t.Logf("Response: %s", status.GetInfo())
})
}

View file

@ -0,0 +1,102 @@
//
// 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 v2
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"k8s.io/apimachinery/pkg/runtime/schema"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
)
func (i *implementation) DiscoverAPIResources(ctx context.Context, in *pbSchedulerV2.SchedulerV2DiscoverAPIResourcesRequest) (*pbSchedulerV2.SchedulerV2DiscoverAPIResourcesResponse, error) {
if in.GetVersion() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Version cannot be empty")
}
resp, err := i.client.DiscoverKubernetesApiVersions(schema.GroupVersion{
Group: in.GetGroup(),
Version: in.GetVersion(),
})
if err != nil {
logger.Err(err).Warn("Unable to run action: DiscoverAPIResources")
return nil, status.Errorf(codes.Internal, "Unable to run action: DiscoverAPIResources: %s", err.Error())
}
resources := make([]*pbSchedulerV2.SchedulerV2DiscoverAPIResource, 0, len(resp))
for _, v := range resp {
resources = append(resources, newKubernetesApiResourceFromDiscoveryResource(v))
}
return &pbSchedulerV2.SchedulerV2DiscoverAPIResourcesResponse{
Resources: resources,
}, nil
}
func (i *implementation) DiscoverAPIResource(ctx context.Context, in *pbSchedulerV2.SchedulerV2DiscoverAPIResourceRequest) (*pbSchedulerV2.SchedulerV2DiscoverAPIResourceResponse, error) {
if in.GetVersion() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Version cannot be empty")
}
if in.GetKind() == "" {
return nil, status.Errorf(codes.InvalidArgument, "Kind cannot be empty")
}
resp, err := i.client.DiscoverKubernetesApiVersionKind(schema.GroupVersionKind{
Group: in.GetGroup(),
Version: in.GetVersion(),
Kind: in.GetKind(),
})
if err != nil {
logger.Err(err).Warn("Unable to run action: DiscoverAPIResource")
return nil, status.Errorf(codes.Internal, "Unable to run action: DiscoverAPIResource: %s", err.Error())
}
if resp != nil {
return &pbSchedulerV2.SchedulerV2DiscoverAPIResourceResponse{
Resource: newKubernetesApiResourceFromDiscoveryResource(*resp),
}, nil
}
return &pbSchedulerV2.SchedulerV2DiscoverAPIResourceResponse{}, nil
}
func (i *implementation) KubernetesGet(ctx context.Context, in *pbSchedulerV2.SchedulerV2KubernetesGetRequest) (*pbSchedulerV2.SchedulerV2KubernetesGetResponse, error) {
reqs := make(helm.Resources, len(in.GetResources()))
for id := range reqs {
reqs[id] = in.GetResources()[id].AsHelmResource()
}
resp, err := i.client.NativeGet(ctx, reqs...)
if err != nil {
logger.Err(err).Warn("Unable to run action: KubernetesGet")
return nil, status.Errorf(codes.Internal, "Unable to run action: KubernetesGet: %s", err.Error())
}
return &pbSchedulerV2.SchedulerV2KubernetesGetResponse{
Objects: newReleaseInfoResourceObjectsFromResourceObjects(resp),
}, nil
}

View file

@ -0,0 +1,25 @@
//
// 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 v2
import "github.com/arangodb/kube-arangodb/pkg/logging"
var logger = logging.Global().RegisterAndGetLogger("integration-scheduler-v2", logging.Info)

View file

@ -0,0 +1,128 @@
//
// 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 v2
import (
"google.golang.org/protobuf/types/known/timestamppb"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
)
func newChartReleaseFromHelmRelease(in *helm.Release) *pbSchedulerV2.SchedulerV2Release {
if in == nil {
return nil
}
var rel pbSchedulerV2.SchedulerV2Release
rel.Name = in.Name
rel.Namespace = in.Namespace
rel.Values = in.Values
rel.Version = int64(in.Version)
rel.Labels = in.Labels
rel.Info = newChartReleaseInfoFromHelmRelease(in.Info)
return &rel
}
func newChartReleaseInfoFromHelmRelease(in helm.ReleaseInfo) *pbSchedulerV2.SchedulerV2ReleaseInfo {
var rel pbSchedulerV2.SchedulerV2ReleaseInfo
rel.FirstDeployed = timestamppb.New(in.FirstDeployed)
rel.LastDeployed = timestamppb.New(in.LastDeployed)
rel.Deleted = timestamppb.New(in.Deleted)
rel.Description = in.Description
rel.Notes = in.Notes
rel.Status = pbSchedulerV2.FromHelmStatus(in.Status)
rel.Resources = newChartReleaseResourcesInfoFromHelmRelease(in.Resources)
return &rel
}
func newChartReleaseResourcesInfoFromHelmRelease(in helm.Resources) []*pbSchedulerV2.SchedulerV2ReleaseInfoResource {
if len(in) == 0 {
return nil
}
var r = make([]*pbSchedulerV2.SchedulerV2ReleaseInfoResource, len(in))
for id := range r {
r[id] = newChartReleaseResourceInfoFromHelmRelease(in[id])
}
return r
}
func newChartReleaseResourceInfoFromHelmRelease(in helm.Resource) *pbSchedulerV2.SchedulerV2ReleaseInfoResource {
var r pbSchedulerV2.SchedulerV2ReleaseInfoResource
r.Gvk = &pbSchedulerV2.SchedulerV2GVK{
Group: in.Group,
Version: in.Version,
Kind: in.Kind,
}
r.Name = in.Name
r.Namespace = in.Namespace
return &r
}
func newKubernetesApiResourceFromDiscoveryResource(in meta.APIResource) *pbSchedulerV2.SchedulerV2DiscoverAPIResource {
return &pbSchedulerV2.SchedulerV2DiscoverAPIResource{
Name: in.Name,
SingularName: in.SingularName,
Namespaced: in.Namespaced,
Group: in.Group,
Version: in.Version,
Kind: in.Kind,
Verbs: in.Verbs,
ShortNames: in.ShortNames,
Categories: in.Categories,
StorageVersionHash: in.StorageVersionHash,
}
}
func newReleaseInfoResourceObjectsFromResourceObjects(in []helm.ResourceObject) []*pbSchedulerV2.SchedulerV2ReleaseInfoResourceObject {
res := make([]*pbSchedulerV2.SchedulerV2ReleaseInfoResourceObject, len(in))
for id := range res {
res[id] = newReleaseInfoResourceObjectFromResourceObject(in[id])
}
return res
}
func newReleaseInfoResourceObjectFromResourceObject(in helm.ResourceObject) *pbSchedulerV2.SchedulerV2ReleaseInfoResourceObject {
var r pbSchedulerV2.SchedulerV2ReleaseInfoResourceObject
r.Resource = newChartReleaseResourceInfoFromHelmRelease(in.Resource)
if d := in.Object; d != nil {
r.Data = &pbSchedulerV2.SchedulerV2ReleaseInfoResourceObjectData{
Data: d.Data,
}
}
return &r
}

Binary file not shown.

View file

@ -0,0 +1,82 @@
//
// 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 v2
import (
"context"
_ "embed"
"os"
"testing"
"github.com/stretchr/testify/require"
"k8s.io/client-go/tools/clientcmd"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
"github.com/arangodb/kube-arangodb/pkg/logging"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
"github.com/arangodb/kube-arangodb/pkg/util/tests"
"github.com/arangodb/kube-arangodb/pkg/util/tests/tgrpc"
)
func init() {
logging.Global().ApplyLogLevels(map[string]logging.Level{
logging.TopicAll: logging.Debug,
})
}
//go:embed suite/example-1.0.0.tgz
var example_1_0_0 []byte
func Handler(t *testing.T, ctx context.Context, client helm.Client, mods ...Mod) svc.Handler {
handler, err := New(client, NewConfiguration().With(mods...))
require.NoError(t, err)
return handler
}
func Client(t *testing.T, ctx context.Context, mods ...Mod) (pbSchedulerV2.SchedulerV2Client, helm.Client) {
z, ok := os.LookupEnv("TEST_KUBECONFIG")
if !ok {
t.Skipf("TEST_KUBECONFIG is not set")
}
cfg, err := clientcmd.BuildConfigFromFlags("", z)
require.NoError(t, err)
client, err := kclient.NewClient("test", cfg)
require.NoError(t, err)
h, err := helm.NewClient(helm.Configuration{
Namespace: tests.FakeNamespace,
Client: client,
})
require.NoError(t, err)
local := svc.NewService(svc.Configuration{
Address: "127.0.0.1:0",
}, Handler(t, ctx, h, mods...))
start := local.Start(ctx)
return tgrpc.NewGRPCClient(t, ctx, pbSchedulerV2.NewSchedulerV2Client, start.Address()), h
}

View file

@ -109,7 +109,7 @@ func (r *Resources) EnsureArangoProfiles(ctx context.Context, cachedStatus inspe
integration, err := sidecar.NewIntegration(&schedulerContainerResourcesApi.Image{
Image: util.NewType(r.context.GetOperatorImage()),
}, spec.Integration.GetSidecar())
}, spec.Integration.GetSidecar(), r.arangoDeploymentProfileTemplate())
if err != nil {
return "", nil, err
}
@ -133,6 +133,8 @@ func (r *Resources) EnsureArangoProfiles(ctx context.Context, cachedStatus inspe
gen(constants.ProfilesIntegrationAuthz, constants.ProfilesIntegrationV0, sidecar.IntegrationAuthorizationV0{}),
gen(constants.ProfilesIntegrationAuthn, constants.ProfilesIntegrationV1, sidecar.IntegrationAuthenticationV1{Spec: spec, DeploymentName: apiObject.GetName()}),
gen(constants.ProfilesIntegrationSched, constants.ProfilesIntegrationV1, sidecar.IntegrationSchedulerV1{}),
gen(constants.ProfilesIntegrationSched, constants.ProfilesIntegrationV2, sidecar.IntegrationSchedulerV2{}),
gen(constants.ProfilesIntegrationEnvoy, constants.ProfilesIntegrationV3, sidecar.IntegrationEnvoyV3{Spec: spec}),
); err != nil {
return err
} else if changed {
@ -142,6 +144,10 @@ func (r *Resources) EnsureArangoProfiles(ctx context.Context, cachedStatus inspe
return reconcileRequired.Reconcile(ctx)
}
func (r *Resources) arangoDeploymentProfileTemplate() *schedulerApi.ProfileTemplate {
return nil
}
func (r *Resources) ensureArangoProfilesFactory(ctx context.Context, cachedStatus inspectorInterface.Inspector, expected ...func() (string, *schedulerApi.ArangoProfile, error)) (bool, error) {
var changed bool

View file

@ -0,0 +1,81 @@
//
// 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 integrations
import (
"context"
"github.com/spf13/cobra"
pbImplSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2"
pbSchedulerV2 "github.com/arangodb/kube-arangodb/integrations/scheduler/v2/definition"
"github.com/arangodb/kube-arangodb/pkg/util/constants"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
"github.com/arangodb/kube-arangodb/pkg/util/k8sutil/helm"
"github.com/arangodb/kube-arangodb/pkg/util/kclient"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
)
func init() {
registerer.Register(pbSchedulerV2.Name, func() Integration {
return &schedulerV2{}
})
}
type schedulerV2 struct {
Configuration pbImplSchedulerV2.Configuration
}
func (b *schedulerV2) Name() string {
return pbSchedulerV2.Name
}
func (b *schedulerV2) Description() string {
return "SchedulerV2 Integration"
}
func (b *schedulerV2) Register(cmd *cobra.Command, fs FlagEnvHandler) error {
return errors.Errors(
fs.StringVar(&b.Configuration.Namespace, "namespace", constants.NamespaceWithDefault("default"), "Kubernetes Namespace"),
fs.StringVar(&b.Configuration.Deployment, "deployment", "", "ArangoDeployment Name"),
)
}
func (b *schedulerV2) Handler(ctx context.Context, cmd *cobra.Command) (svc.Handler, error) {
client, ok := kclient.GetDefaultFactory().Client()
if !ok {
return nil, errors.Errorf("Unable to create Kubernetes Client")
}
helm, err := helm.NewClient(helm.Configuration{
Namespace: b.Configuration.Namespace,
Client: client,
})
if err != nil {
return nil, errors.Wrapf(err, "Unable to create Helm Client")
}
return pbImplSchedulerV2.New(helm, b.Configuration)
}
func (*schedulerV2) Init(ctx context.Context, cmd *cobra.Command) error {
return nil
}

View file

@ -116,7 +116,7 @@ func NewIntegrationEnablement(integrations ...Integration) (*schedulerApi.Profil
}, nil
}
func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *schedulerIntegrationApi.Sidecar) (*schedulerApi.ProfileTemplate, error) {
func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *schedulerIntegrationApi.Sidecar, profiles ...*schedulerApi.ProfileTemplate) (*schedulerApi.ProfileTemplate, error) {
// Arguments
exePath := k8sutil.BinaryPath()
@ -212,5 +212,11 @@ func NewIntegration(image *schedulerContainerResourcesApi.Image, integration *sc
Env: envs,
}
return &pt, nil
res := &pt
for _, prof := range profiles {
res = res.With(prof)
}
return res, nil
}

View file

@ -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 sidecar
import (
core "k8s.io/api/core/v1"
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
)
type IntegrationSchedulerV2 struct {
Core *Core
DeploymentName string
Spec api.DeploymentSpec
}
func (i IntegrationSchedulerV2) Name() []string {
return []string{"SCHEDULER", "V2"}
}
func (i IntegrationSchedulerV2) Validate() error {
return nil
}
func (i IntegrationSchedulerV2) Envs() ([]core.EnvVar, error) {
var envs = []core.EnvVar{
{
Name: "INTEGRATION_SCHEDULER_V2",
Value: "true",
},
{
Name: "INTEGRATION_SCHEDULER_V2_NAMESPACE",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
FieldPath: "metadata.namespace",
},
},
},
{
Name: "INTEGRATION_SCHEDULER_V2_DEPLOYMENT",
Value: i.DeploymentName,
},
}
return i.Core.Envs(i, envs...), nil
}
func (i IntegrationSchedulerV2) GlobalEnvs() ([]core.EnvVar, error) {
return nil, nil
}
func (i IntegrationSchedulerV2) Volumes() ([]core.Volume, []core.VolumeMount, error) {
return nil, nil, nil
}

View file

@ -31,11 +31,14 @@ const (
ProfilesIntegrationAuthn = "authn"
ProfilesIntegrationAuthz = "authz"
ProfilesIntegrationSched = "sched"
ProfilesIntegrationEnvoy = "envoy"
)
const (
ProfilesIntegrationV0 = "v0"
ProfilesIntegrationV1 = "v1"
ProfilesIntegrationV2 = "v2"
ProfilesIntegrationV3 = "v3"
)
func NewProfileIntegration(name, version string) (string, string) {