From cce50b32d07e8ca77b89f8c3aae971046e7695b9 Mon Sep 17 00:00:00 2001 From: Adam Janikowski <12255597+ajanikow@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:12:40 +0100 Subject: [PATCH] [Maintenance] Extract GRPC Package for compatibility (#1763) --- .golangci.yaml | 2 + CHANGELOG.md | 1 + pkg/apis/deployment/v1/compatibility_test.go | 30 +++++++++ .../v1/server_group_volume_mount.go | 67 ++++++++++++++++++- .../deployment/v1/zz_generated.deepcopy.go | 5 -- .../deployment/v2alpha1/compatibility_test.go | 30 +++++++++ .../v2alpha1/server_group_volume_mount.go | 67 ++++++++++++++++++- .../v2alpha1/zz_generated.deepcopy.go | 5 -- pkg/integrations/auth.go | 4 +- pkg/integrations/clients/client.go | 9 +-- pkg/integrations/envoy_auth_v3.go | 4 +- pkg/util/{ => grpc}/grpc.go | 7 +- pkg/util/tests/tgrpc/grpc.go | 6 +- pkg/util/tests/types/compatibility.go | 60 +++++++++++++++++ 14 files changed, 267 insertions(+), 30 deletions(-) create mode 100644 pkg/apis/deployment/v1/compatibility_test.go create mode 100644 pkg/apis/deployment/v2alpha1/compatibility_test.go rename pkg/util/{ => grpc}/grpc.go (96%) create mode 100644 pkg/util/tests/types/compatibility.go diff --git a/.golangci.yaml b/.golangci.yaml index 64b90f524..b307bfe57 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -133,6 +133,8 @@ linters-settings: pkg: k8s.io/apimachinery/pkg/apis/meta/v1 - alias: typedCore pkg: k8s.io/client-go/kubernetes/typed/core/v1 + - alias: ugrpc + pkg: github.com/arangodb/kube-arangodb/pkg/util/grpc gci: sections: - standard diff --git a/CHANGELOG.md b/CHANGELOG.md index d26b5cbdc..62ec2706f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - (Feature) (Scheduler) Helm Driver Param - (Feature) (Integration) Services Endpoint - (Feature) (Platform) Storage +- (Maintenance) Extract GRPC Client Package ## [1.2.43](https://github.com/arangodb/kube-arangodb/tree/1.2.43) (2024-10-14) - (Feature) ArangoRoute CRD diff --git a/pkg/apis/deployment/v1/compatibility_test.go b/pkg/apis/deployment/v1/compatibility_test.go new file mode 100644 index 000000000..090229555 --- /dev/null +++ b/pkg/apis/deployment/v1/compatibility_test.go @@ -0,0 +1,30 @@ +// +// 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 v1 + +import ( + "testing" +) + +func Test_ServerGroupSpecVolumeMount(t *testing.T) { + // TODO: Uncomment after upgrade to 1.31.1+ + // require.NoError(t, types.EnsureTypeForwardCompatibility(reflect.TypeOf(core.VolumeMount{}), reflect.TypeOf(ServerGroupSpecVolumeMount{}))) +} diff --git a/pkg/apis/deployment/v1/server_group_volume_mount.go b/pkg/apis/deployment/v1/server_group_volume_mount.go index bb767f040..a4400bf83 100644 --- a/pkg/apis/deployment/v1/server_group_volume_mount.go +++ b/pkg/apis/deployment/v1/server_group_volume_mount.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-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. @@ -55,10 +55,71 @@ func (s ServerGroupSpecVolumeMounts) Validate() error { return shared.WithErrors(validateErrors...) } -type ServerGroupSpecVolumeMount core.VolumeMount +type ServerGroupSpecVolumeMount struct { + // This must match the Name of a Volume. + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + // Mounted read-only if true, read-write otherwise (false or unspecified). + // Defaults to false. + // +optional + + ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,2,opt,name=readOnly"` + // RecursiveReadOnly specifies whether read-only mounts should be handled + // recursively. + // + // If ReadOnly is false, this field has no meaning and must be unspecified. + // + // If ReadOnly is true, and this field is set to Disabled, the mount is not made + // recursively read-only. If this field is set to IfPossible, the mount is made + // recursively read-only, if it is supported by the container runtime. If this + // field is set to Enabled, the mount is made recursively read-only if it is + // supported by the container runtime, otherwise the pod will not be started and + // an error will be generated to indicate the reason. + // + // If this field is set to IfPossible or Enabled, MountPropagation must be set to + // None (or be unspecified, which defaults to None). + // + // If this field is not specified, it is treated as an equivalent of Disabled. + // + // +featureGate=RecursiveReadOnlyMounts + // +optional + // TODO: Uncomment after upgrade to 1.31.1+ + // RecursiveReadOnly *RecursiveReadOnlyMode `json:"recursiveReadOnly,omitempty" protobuf:"bytes,7,opt,name=recursiveReadOnly,casttype=RecursiveReadOnlyMode"` + + // Path within the container at which the volume should be mounted. Must + // not contain ':'. + MountPath string `json:"mountPath" protobuf:"bytes,3,opt,name=mountPath"` + // Path within the volume from which the container's volume should be mounted. + // Defaults to "" (volume's root). + // +optional + + SubPath string `json:"subPath,omitempty" protobuf:"bytes,4,opt,name=subPath"` + // mountPropagation determines how mounts are propagated from the host + // to container and the other way around. + // When not set, MountPropagationNone is used. + // This field is beta in 1.10. + // When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + // (which defaults to None). + // +optional + + MountPropagation *core.MountPropagationMode `json:"mountPropagation,omitempty" protobuf:"bytes,5,opt,name=mountPropagation,casttype=MountPropagationMode"` + // Expanded path within the volume from which the container's volume should be mounted. + // Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + // Defaults to "" (volume's root). + // SubPathExpr and SubPath are mutually exclusive. + // +optional + SubPathExpr string `json:"subPathExpr,omitempty" protobuf:"bytes,6,opt,name=subPathExpr"` +} func (s ServerGroupSpecVolumeMount) VolumeMount() core.VolumeMount { - return core.VolumeMount(s) + return core.VolumeMount{ + Name: s.Name, + ReadOnly: s.ReadOnly, + //RecursiveReadOnly: s.RecursiveReadOnly, TODO: Uncomment after upgrade to 1.31.1+ + MountPath: s.MountPath, + SubPath: s.SubPath, + MountPropagation: s.MountPropagation, + SubPathExpr: s.SubPathExpr, + } } func (s *ServerGroupSpecVolumeMount) Validate() error { diff --git a/pkg/apis/deployment/v1/zz_generated.deepcopy.go b/pkg/apis/deployment/v1/zz_generated.deepcopy.go index d683e6d9d..86f9e84bf 100644 --- a/pkg/apis/deployment/v1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v1/zz_generated.deepcopy.go @@ -2907,11 +2907,6 @@ func (in *ServerGroupSpecVolumeHostPath) DeepCopy() *ServerGroupSpecVolumeHostPa // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerGroupSpecVolumeMount) DeepCopyInto(out *ServerGroupSpecVolumeMount) { *out = *in - if in.RecursiveReadOnly != nil { - in, out := &in.RecursiveReadOnly, &out.RecursiveReadOnly - *out = new(corev1.RecursiveReadOnlyMode) - **out = **in - } if in.MountPropagation != nil { in, out := &in.MountPropagation, &out.MountPropagation *out = new(corev1.MountPropagationMode) diff --git a/pkg/apis/deployment/v2alpha1/compatibility_test.go b/pkg/apis/deployment/v2alpha1/compatibility_test.go new file mode 100644 index 000000000..92ff8afe5 --- /dev/null +++ b/pkg/apis/deployment/v2alpha1/compatibility_test.go @@ -0,0 +1,30 @@ +// +// 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 v2alpha1 + +import ( + "testing" +) + +func Test_ServerGroupSpecVolumeMount(t *testing.T) { + // TODO: Uncomment after upgrade to 1.31.1+ + // require.NoError(t, types.EnsureTypeForwardCompatibility(reflect.TypeOf(core.VolumeMount{}), reflect.TypeOf(ServerGroupSpecVolumeMount{}))) +} diff --git a/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go b/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go index 2d0fddd66..18ebba5bf 100644 --- a/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go +++ b/pkg/apis/deployment/v2alpha1/server_group_volume_mount.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-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. @@ -55,10 +55,71 @@ func (s ServerGroupSpecVolumeMounts) Validate() error { return shared.WithErrors(validateErrors...) } -type ServerGroupSpecVolumeMount core.VolumeMount +type ServerGroupSpecVolumeMount struct { + // This must match the Name of a Volume. + Name string `json:"name" protobuf:"bytes,1,opt,name=name"` + // Mounted read-only if true, read-write otherwise (false or unspecified). + // Defaults to false. + // +optional + + ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,2,opt,name=readOnly"` + // RecursiveReadOnly specifies whether read-only mounts should be handled + // recursively. + // + // If ReadOnly is false, this field has no meaning and must be unspecified. + // + // If ReadOnly is true, and this field is set to Disabled, the mount is not made + // recursively read-only. If this field is set to IfPossible, the mount is made + // recursively read-only, if it is supported by the container runtime. If this + // field is set to Enabled, the mount is made recursively read-only if it is + // supported by the container runtime, otherwise the pod will not be started and + // an error will be generated to indicate the reason. + // + // If this field is set to IfPossible or Enabled, MountPropagation must be set to + // None (or be unspecified, which defaults to None). + // + // If this field is not specified, it is treated as an equivalent of Disabled. + // + // +featureGate=RecursiveReadOnlyMounts + // +optional + // TODO: Uncomment after upgrade to 1.31.1+ + // RecursiveReadOnly *RecursiveReadOnlyMode `json:"recursiveReadOnly,omitempty" protobuf:"bytes,7,opt,name=recursiveReadOnly,casttype=RecursiveReadOnlyMode"` + + // Path within the container at which the volume should be mounted. Must + // not contain ':'. + MountPath string `json:"mountPath" protobuf:"bytes,3,opt,name=mountPath"` + // Path within the volume from which the container's volume should be mounted. + // Defaults to "" (volume's root). + // +optional + + SubPath string `json:"subPath,omitempty" protobuf:"bytes,4,opt,name=subPath"` + // mountPropagation determines how mounts are propagated from the host + // to container and the other way around. + // When not set, MountPropagationNone is used. + // This field is beta in 1.10. + // When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + // (which defaults to None). + // +optional + + MountPropagation *core.MountPropagationMode `json:"mountPropagation,omitempty" protobuf:"bytes,5,opt,name=mountPropagation,casttype=MountPropagationMode"` + // Expanded path within the volume from which the container's volume should be mounted. + // Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. + // Defaults to "" (volume's root). + // SubPathExpr and SubPath are mutually exclusive. + // +optional + SubPathExpr string `json:"subPathExpr,omitempty" protobuf:"bytes,6,opt,name=subPathExpr"` +} func (s ServerGroupSpecVolumeMount) VolumeMount() core.VolumeMount { - return core.VolumeMount(s) + return core.VolumeMount{ + Name: s.Name, + ReadOnly: s.ReadOnly, + //RecursiveReadOnly: s.RecursiveReadOnly, TODO: Uncomment after upgrade to 1.31.1+ + MountPath: s.MountPath, + SubPath: s.SubPath, + MountPropagation: s.MountPropagation, + SubPathExpr: s.SubPathExpr, + } } func (s *ServerGroupSpecVolumeMount) Validate() error { diff --git a/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go index 7cbbb932d..c57528ba7 100644 --- a/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/deployment/v2alpha1/zz_generated.deepcopy.go @@ -2907,11 +2907,6 @@ func (in *ServerGroupSpecVolumeHostPath) DeepCopy() *ServerGroupSpecVolumeHostPa // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerGroupSpecVolumeMount) DeepCopyInto(out *ServerGroupSpecVolumeMount) { *out = *in - if in.RecursiveReadOnly != nil { - in, out := &in.RecursiveReadOnly, &out.RecursiveReadOnly - *out = new(v1.RecursiveReadOnlyMode) - **out = **in - } if in.MountPropagation != nil { in, out := &in.MountPropagation, &out.MountPropagation *out = new(v1.MountPropagationMode) diff --git a/pkg/integrations/auth.go b/pkg/integrations/auth.go index 027f1f83b..c7ae54ad2 100644 --- a/pkg/integrations/auth.go +++ b/pkg/integrations/auth.go @@ -28,7 +28,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "github.com/arangodb/kube-arangodb/pkg/util" + ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc" ) func basicTokenAuthAuthorize(ctx context.Context, token string) error { @@ -37,7 +37,7 @@ func basicTokenAuthAuthorize(ctx context.Context, token string) error { return status.Errorf(codes.Unauthenticated, "metadata is not provided") } - values := md[util.AuthorizationGRPCHeader] + values := md[ugrpc.AuthorizationGRPCHeader] if len(values) == 0 { return status.Errorf(codes.Unauthenticated, "authorization token is not provided") } diff --git a/pkg/integrations/clients/client.go b/pkg/integrations/clients/client.go index 0b59a8016..7e78abcae 100644 --- a/pkg/integrations/clients/client.go +++ b/pkg/integrations/clients/client.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc" "github.com/arangodb/kube-arangodb/pkg/util" + ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc" "github.com/arangodb/kube-arangodb/pkg/util/shutdown" ) @@ -74,7 +75,7 @@ func client[T any](ctx context.Context, cfg *Config, in func(cc grpc.ClientConnI var opts []grpc.DialOption if token := cfg.Token; token != "" { - opts = append(opts, util.TokenAuthInterceptors(token)...) + opts = append(opts, ugrpc.TokenAuthInterceptors(token)...) } if cfg.TLS.Enabled { @@ -99,18 +100,18 @@ func client[T any](ctx context.Context, cfg *Config, in func(cc grpc.ClientConnI } if cfg.TLS.Fallback { - client, closer, err := util.NewOptionalTLSGRPCClient(ctx, in, cfg.Address, config, opts...) + client, closer, err := ugrpc.NewOptionalTLSGRPCClient(ctx, in, cfg.Address, config, opts...) if err != nil { return util.Default[T](), nil, err } return client, closer, nil } else { - opts = append(opts, util.ClientTLS(config)...) + opts = append(opts, ugrpc.ClientTLS(config)...) } } - client, closer, err := util.NewGRPCClient(ctx, in, cfg.Address, opts...) + client, closer, err := ugrpc.NewGRPCClient(ctx, in, cfg.Address, opts...) if err != nil { return util.Default[T](), nil, err } diff --git a/pkg/integrations/envoy_auth_v3.go b/pkg/integrations/envoy_auth_v3.go index 4fbdcfff6..10fcd59cc 100644 --- a/pkg/integrations/envoy_auth_v3.go +++ b/pkg/integrations/envoy_auth_v3.go @@ -27,7 +27,7 @@ import ( pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition" pbImplEnvoyAuthV3 "github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3" - "github.com/arangodb/kube-arangodb/pkg/util" + ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc" "github.com/arangodb/kube-arangodb/pkg/util/svc" ) @@ -60,7 +60,7 @@ func (a *envoyAuthV3) Handler(ctx context.Context, cmd *cobra.Command) (svc.Hand return nil, err } - c, _, err := util.NewGRPCClient(ctx, pbAuthenticationV1.NewAuthenticationV1Client, v) + c, _, err := ugrpc.NewGRPCClient(ctx, pbAuthenticationV1.NewAuthenticationV1Client, v) if err != nil { return nil, err } diff --git a/pkg/util/grpc.go b/pkg/util/grpc/grpc.go similarity index 96% rename from pkg/util/grpc.go rename to pkg/util/grpc/grpc.go index b5c1f9685..217f54514 100644 --- a/pkg/util/grpc.go +++ b/pkg/util/grpc/grpc.go @@ -18,7 +18,7 @@ // Copyright holder is ArangoDB GmbH, Cologne, Germany // -package util +package grpc import ( "context" @@ -35,6 +35,7 @@ import ( pbPongV1 "github.com/arangodb/kube-arangodb/integrations/pong/v1/definition" pbSharedV1 "github.com/arangodb/kube-arangodb/integrations/shared/v1/definition" + "github.com/arangodb/kube-arangodb/pkg/util" "github.com/arangodb/kube-arangodb/pkg/util/svc" ) @@ -43,7 +44,7 @@ const AuthorizationGRPCHeader = "adb-authorization" func NewGRPCClient[T any](ctx context.Context, in func(cc grpc.ClientConnInterface) T, addr string, opts ...grpc.DialOption) (T, io.Closer, error) { con, err := NewGRPCConn(addr, opts...) if err != nil { - return Default[T](), nil, err + return util.Default[T](), nil, err } return in(con), con, nil @@ -52,7 +53,7 @@ func NewGRPCClient[T any](ctx context.Context, in func(cc grpc.ClientConnInterfa func NewOptionalTLSGRPCClient[T any](ctx context.Context, in func(cc grpc.ClientConnInterface) T, addr string, tls *tls.Config, opts ...grpc.DialOption) (T, io.Closer, error) { con, err := NewOptionalTLSGRPCConn(ctx, addr, tls, opts...) if err != nil { - return Default[T](), nil, err + return util.Default[T](), nil, err } return in(con), con, nil diff --git a/pkg/util/tests/tgrpc/grpc.go b/pkg/util/tests/tgrpc/grpc.go index a977c22c0..678b78430 100644 --- a/pkg/util/tests/tgrpc/grpc.go +++ b/pkg/util/tests/tgrpc/grpc.go @@ -32,12 +32,12 @@ import ( "google.golang.org/grpc/status" proto "google.golang.org/protobuf/proto" - "github.com/arangodb/kube-arangodb/pkg/util" + ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc" "github.com/arangodb/kube-arangodb/pkg/util/svc" ) func NewGRPCClient[T any](t *testing.T, ctx context.Context, in func(cc grpc.ClientConnInterface) T, addr string, opts ...grpc.DialOption) T { - client, closer, err := util.NewGRPCClient(ctx, in, addr, opts...) + client, closer, err := ugrpc.NewGRPCClient(ctx, in, addr, opts...) require.NoError(t, err) go func() { <-ctx.Done() @@ -76,5 +76,5 @@ func AsGRPCError(t *testing.T, err error) ErrorStatusValidator { } func GRPCAnyCastAs[T proto.Message](t *testing.T, in *any1.Any, v T) { - require.NoError(t, util.GRPCAnyCastAs[T](in, v)) + require.NoError(t, ugrpc.GRPCAnyCastAs[T](in, v)) } diff --git a/pkg/util/tests/types/compatibility.go b/pkg/util/tests/types/compatibility.go new file mode 100644 index 000000000..fb70a9b67 --- /dev/null +++ b/pkg/util/tests/types/compatibility.go @@ -0,0 +1,60 @@ +// +// 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 types + +import ( + "reflect" + + "github.com/pkg/errors" + + shared "github.com/arangodb/kube-arangodb/pkg/apis/shared" +) + +func EnsureTypeForwardCompatibility(a, b reflect.Type) error { + if a.Kind() != b.Kind() { + return errors.Errorf("Invalid kind, got %s, expected %s", a.Kind().String(), b.Kind().String()) + } + + if a == b { + return nil + } + + switch a.Kind() { + case reflect.Struct: + var err = make([]error, 0, a.NumField()) + + for id := 0; id < a.NumField(); id++ { + f := a.Field(id) + + bf, ok := b.FieldByName(f.Name) + if !ok { + err = append(err, shared.PrefixResourceError(f.Name, errors.Errorf("Field not defined in target"))) + continue + } + + err = append(err, shared.PrefixResourceError(f.Name, EnsureTypeForwardCompatibility(f.Type, bf.Type))) + } + + return shared.WithErrors(err...) + default: + return nil + } +}