From a4665ea6f0781d65da3fc0aa1e74e01cdca393e0 Mon Sep 17 00:00:00 2001
From: ajanikow <12255597+ajanikow@users.noreply.github.com>
Date: Tue, 30 Apr 2024 12:54:59 +0000
Subject: [PATCH] [Feature] [ML] Add TLS Secrets
---
CHANGELOG.md | 1 +
docs/api/ArangoMLExtension.V1Alpha1.md | 48 +++++++++++++++++
.../ml/v1alpha1/extension_spec_deployment.go | 10 ++++
.../v1alpha1/extension_spec_deployment_tls.go | 29 ++++++++++
.../v1alpha1/extension_status_arangodb_ref.go | 4 +-
pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go | 36 +++++++++++++
pkg/apis/shared/v1/object.go | 14 +++++
.../crds/ml-extension.schema.generated.yaml | 12 +++++
pkg/deployment/resources/certificates_tls.go | 5 +-
pkg/util/context.go | 35 +++++++++++-
pkg/util/k8sutil/kerrors/errors.go | 53 ++++++++++++++++++-
pkg/util/k8sutil/secrets.go | 9 ++--
12 files changed, 247 insertions(+), 9 deletions(-)
create mode 100644 pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b89f6737..8eebbddb3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
- (Bugfix) Ensure PDB is created
- (Bugfix) Fix Schema Apply Checksum
- (Bugfix) Use MD5 instead of SHA256 for CRD Checksums
+- (Feature) (ML) Add TLS Secrets
## [1.2.40](https://github.com/arangodb/kube-arangodb/tree/1.2.40) (2024-04-10)
- (Feature) Add Core fields to the Scheduler Container Spec
diff --git a/docs/api/ArangoMLExtension.V1Alpha1.md b/docs/api/ArangoMLExtension.V1Alpha1.md
index 0de026b0f..5333d9866 100644
--- a/docs/api/ArangoMLExtension.V1Alpha1.md
+++ b/docs/api/ArangoMLExtension.V1Alpha1.md
@@ -567,6 +567,22 @@ Default Value: `false`
***
+### .spec.deployment.tls.altNames
+
+Type: `array` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go#L28)
+
+AltNames define TLS AltNames used when TLS on the ArangoDB is enabled
+
+***
+
+### .spec.deployment.tls.enabled
+
+Type: `boolean` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go#L25)
+
+Enabled define if TLS Should be enabled. If is not set then default is taken from ArangoDeployment settings
+
+***
+
### .spec.deployment.tolerations
Type: `[]core.Toleration` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/scheduler/v1alpha1/pod/resources/scheduling.go#L49)
@@ -3317,6 +3333,38 @@ UID keeps the information about object UID
***
+### .status.arangoDB.tls.checksum
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/shared/v1/object.go#L61)
+
+UID keeps the information about object Checksum
+
+***
+
+### .status.arangoDB.tls.name
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/shared/v1/object.go#L52)
+
+Name of the object
+
+***
+
+### .status.arangoDB.tls.namespace
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/shared/v1/object.go#L55)
+
+Namespace of the object. Should default to the namespace of the parent object
+
+***
+
+### .status.arangoDB.tls.uid
+
+Type: `string` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/shared/v1/object.go#L58)
+
+UID keeps the information about object UID
+
+***
+
### .status.conditions
Type: `api.Conditions` [\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.40/pkg/apis/ml/v1alpha1/extension_status.go#L31)
diff --git a/pkg/apis/ml/v1alpha1/extension_spec_deployment.go b/pkg/apis/ml/v1alpha1/extension_spec_deployment.go
index fc6d7543f..4fa08fb80 100644
--- a/pkg/apis/ml/v1alpha1/extension_spec_deployment.go
+++ b/pkg/apis/ml/v1alpha1/extension_spec_deployment.go
@@ -58,6 +58,9 @@ type ArangoMLExtensionSpecDeployment struct {
// Service defines how components will be exposed
Service *ArangoMLExtensionSpecDeploymentService `json:"service,omitempty"`
+ // TLS defined TLS Settings for extension
+ TLS *ArangoMLExtensionSpecDeploymentTLS `json:"tls,omitempty"`
+
// Pod defines base template for pods
*schedulerPodApi.Pod
@@ -136,6 +139,13 @@ func (s *ArangoMLExtensionSpecDeployment) GetService() *ArangoMLExtensionSpecDep
return s.Service
}
+func (s *ArangoMLExtensionSpecDeployment) GetTLS() *ArangoMLExtensionSpecDeploymentTLS {
+ if s == nil {
+ return nil
+ }
+ return s.TLS
+}
+
func (s *ArangoMLExtensionSpecDeployment) Validate() error {
if s == nil {
return nil
diff --git a/pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go b/pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go
new file mode 100644
index 000000000..242b14c1f
--- /dev/null
+++ b/pkg/apis/ml/v1alpha1/extension_spec_deployment_tls.go
@@ -0,0 +1,29 @@
+//
+// 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
+
+type ArangoMLExtensionSpecDeploymentTLS struct {
+ // Enabled define if TLS Should be enabled. If is not set then default is taken from ArangoDeployment settings
+ Enabled *bool `json:"enabled,omitempty"`
+
+ // AltNames define TLS AltNames used when TLS on the ArangoDB is enabled
+ AltNames []string `json:"altNames,omitempty"`
+}
diff --git a/pkg/apis/ml/v1alpha1/extension_status_arangodb_ref.go b/pkg/apis/ml/v1alpha1/extension_status_arangodb_ref.go
index 9ce7b78b9..b51e5a127 100644
--- a/pkg/apis/ml/v1alpha1/extension_status_arangodb_ref.go
+++ b/pkg/apis/ml/v1alpha1/extension_status_arangodb_ref.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.
@@ -25,6 +25,8 @@ import sharedApi "github.com/arangodb/kube-arangodb/pkg/apis/shared/v1"
type ArangoMLExtensionStatusArangoDBRef struct {
// Secret keeps the information about ArangoDB deployment
Secret *sharedApi.Object `json:"secret,omitempty"`
+ // TLS keeps information about TLS Secret rendered from ArangoDB deployment
+ TLS *sharedApi.Object `json:"tls,omitempty"`
// JWTTokenSecret keeps the JWT for ArangoDB authentication (only when ArangoDeployment has JWT enabled)
JWTTokenSecret *sharedApi.Object `json:"jwtTokenSecret,omitempty"`
}
diff --git a/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go
index ef431a0ce..c9f71c96b 100644
--- a/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/ml/v1alpha1/zz_generated.deepcopy.go
@@ -380,6 +380,11 @@ func (in *ArangoMLExtensionSpecDeployment) DeepCopyInto(out *ArangoMLExtensionSp
*out = new(ArangoMLExtensionSpecDeploymentService)
(*in).DeepCopyInto(*out)
}
+ if in.TLS != nil {
+ in, out := &in.TLS, &out.TLS
+ *out = new(ArangoMLExtensionSpecDeploymentTLS)
+ (*in).DeepCopyInto(*out)
+ }
if in.Pod != nil {
in, out := &in.Pod, &out.Pod
*out = new(pod.Pod)
@@ -465,6 +470,32 @@ func (in *ArangoMLExtensionSpecDeploymentService) DeepCopy() *ArangoMLExtensionS
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ArangoMLExtensionSpecDeploymentTLS) DeepCopyInto(out *ArangoMLExtensionSpecDeploymentTLS) {
+ *out = *in
+ if in.Enabled != nil {
+ in, out := &in.Enabled, &out.Enabled
+ *out = new(bool)
+ **out = **in
+ }
+ if in.AltNames != nil {
+ in, out := &in.AltNames, &out.AltNames
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArangoMLExtensionSpecDeploymentTLS.
+func (in *ArangoMLExtensionSpecDeploymentTLS) DeepCopy() *ArangoMLExtensionSpecDeploymentTLS {
+ if in == nil {
+ return nil
+ }
+ out := new(ArangoMLExtensionSpecDeploymentTLS)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ArangoMLExtensionSpecMetadataService) DeepCopyInto(out *ArangoMLExtensionSpecMetadataService) {
*out = *in
@@ -563,6 +594,11 @@ func (in *ArangoMLExtensionStatusArangoDBRef) DeepCopyInto(out *ArangoMLExtensio
*out = new(sharedv1.Object)
(*in).DeepCopyInto(*out)
}
+ if in.TLS != nil {
+ in, out := &in.TLS, &out.TLS
+ *out = new(sharedv1.Object)
+ (*in).DeepCopyInto(*out)
+ }
if in.JWTTokenSecret != nil {
in, out := &in.JWTTokenSecret, &out.JWTTokenSecret
*out = new(sharedv1.Object)
diff --git a/pkg/apis/shared/v1/object.go b/pkg/apis/shared/v1/object.go
index fd1ec59a1..e9cc5adf0 100644
--- a/pkg/apis/shared/v1/object.go
+++ b/pkg/apis/shared/v1/object.go
@@ -94,6 +94,20 @@ func (o *Object) GetUID() types.UID {
return ""
}
+func (o *Object) AsUIDPrecondition() *meta.Preconditions {
+ if o == nil || o.UID == nil {
+ return nil
+ }
+
+ uid := o.GetUID()
+
+ if uid == "" {
+ return nil
+ }
+
+ return meta.NewUIDPreconditions(string(uid))
+}
+
func (o *Object) GetChecksum() string {
if o != nil {
if n := o.Checksum; n != nil {
diff --git a/pkg/crd/crds/ml-extension.schema.generated.yaml b/pkg/crd/crds/ml-extension.schema.generated.yaml
index 35ea28a4a..2b04e092a 100644
--- a/pkg/crd/crds/ml-extension.schema.generated.yaml
+++ b/pkg/crd/crds/ml-extension.schema.generated.yaml
@@ -1441,6 +1441,18 @@ v1alpha1:
type: string
shareProcessNamespace:
type: boolean
+ tls:
+ description: TLS defined TLS Settings for extension
+ properties:
+ altNames:
+ description: AltNames define TLS AltNames used when TLS on the ArangoDB is enabled
+ items:
+ type: string
+ type: array
+ enabled:
+ description: Enabled define if TLS Should be enabled. If is not set then default is taken from ArangoDeployment settings
+ type: boolean
+ type: object
tolerations:
items:
properties:
diff --git a/pkg/deployment/resources/certificates_tls.go b/pkg/deployment/resources/certificates_tls.go
index 0465e53e1..d7ad24b4d 100644
--- a/pkg/deployment/resources/certificates_tls.go
+++ b/pkg/deployment/resources/certificates_tls.go
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
-// Copyright 2016-2022 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.
@@ -113,7 +113,8 @@ func createTLSServerCertificate(ctx context.Context, log logging.Logger, cachedS
strings.TrimSpace(priv)
err = globals.GetGlobalTimeouts().Kubernetes().RunWithTimeout(ctx, func(ctxChild context.Context) error {
- return k8sutil.CreateTLSKeyfileSecret(ctxChild, secrets, secretName, keyfile, ownerRef)
+ _, err := k8sutil.CreateTLSKeyfileSecret(ctxChild, secrets, secretName, keyfile, ownerRef)
+ return err
})
if err != nil {
if kerrors.IsAlreadyExists(err) {
diff --git a/pkg/util/context.go b/pkg/util/context.go
index 380d1dca4..16bb027cf 100644
--- a/pkg/util/context.go
+++ b/pkg/util/context.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.
@@ -64,6 +64,39 @@ func WithContextTimeoutP2A2[P1, P2, A1, A2 interface{}](ctx context.Context, tim
return f(nCtx, a1, a2)
}
+func WithKubernetesContextTimeoutP1A4[P1, A1, A2, A3, A4 interface{}](ctx context.Context, f func(context.Context, A1, A2, A3, A4) P1, a1 A1, a2 A2, a3 A3, a4 A4) P1 {
+ return WithContextTimeoutP1A4(ctx, globals.GetGlobals().Timeouts().Kubernetes().Get(), f, a1, a2, a3, a4)
+}
+
+func WithContextTimeoutP1A4[P1, A1, A2, A3, A4 interface{}](ctx context.Context, timeout time.Duration, f func(context.Context, A1, A2, A3, A4) P1, a1 A1, a2 A2, a3 A3, a4 A4) P1 {
+ nCtx, c := context.WithTimeout(ctx, timeout)
+ defer c()
+
+ return f(nCtx, a1, a2, a3, a4)
+}
+
+func WithKubernetesContextTimeoutP2A4[P1, P2, A1, A2, A3, A4 interface{}](ctx context.Context, f func(context.Context, A1, A2, A3, A4) (P1, P2), a1 A1, a2 A2, a3 A3, a4 A4) (P1, P2) {
+ return WithContextTimeoutP2A4(ctx, globals.GetGlobals().Timeouts().Kubernetes().Get(), f, a1, a2, a3, a4)
+}
+
+func WithContextTimeoutP2A4[P1, P2, A1, A2, A3, A4 interface{}](ctx context.Context, timeout time.Duration, f func(context.Context, A1, A2, A3, A4) (P1, P2), a1 A1, a2 A2, a3 A3, a4 A4) (P1, P2) {
+ nCtx, c := context.WithTimeout(ctx, timeout)
+ defer c()
+
+ return f(nCtx, a1, a2, a3, a4)
+}
+
+func WithKubernetesContextTimeoutP4A3[P1, P2, P3, P4, A1, A2, A3 interface{}](ctx context.Context, f func(context.Context, A1, A2, A3) (P1, P2, P3, P4), a1 A1, a2 A2, a3 A3) (P1, P2, P3, P4) {
+ return WithContextTimeoutP4A3(ctx, globals.GetGlobals().Timeouts().Kubernetes().Get(), f, a1, a2, a3)
+}
+
+func WithContextTimeoutP4A3[P1, P2, P3, P4, A1, A2, A3 interface{}](ctx context.Context, timeout time.Duration, f func(context.Context, A1, A2, A3) (P1, P2, P3, P4), a1 A1, a2 A2, a3 A3) (P1, P2, P3, P4) {
+ nCtx, c := context.WithTimeout(ctx, timeout)
+ defer c()
+
+ return f(nCtx, a1, a2, a3)
+}
+
type PatchInterface[P1 meta.Object] interface {
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts meta.PatchOptions, subresources ...string) (P1, error)
}
diff --git a/pkg/util/k8sutil/kerrors/errors.go b/pkg/util/k8sutil/kerrors/errors.go
index d599dc92a..98b76fe43 100644
--- a/pkg/util/k8sutil/kerrors/errors.go
+++ b/pkg/util/k8sutil/kerrors/errors.go
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
-// Copyright 2016-2022 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.
@@ -26,6 +26,47 @@ import (
"github.com/arangodb/kube-arangodb/pkg/util/errors"
)
+func Is(err error, codes ...KErrors) bool {
+ if err == nil {
+ return false
+ }
+
+ for _, code := range codes {
+ if code.Is(err) {
+ return true
+ }
+ }
+
+ return false
+}
+
+type KErrors int
+
+const (
+ AlreadyExists KErrors = iota
+ Conflict
+ Invalid
+ NotFound
+ Forbidden
+)
+
+func (k KErrors) Is(err error) bool {
+ switch k {
+ case AlreadyExists:
+ return IsAlreadyExists(err)
+ case Conflict:
+ return IsConflict(err)
+ case Invalid:
+ return IsInvalid(err)
+ case NotFound:
+ return IsNotFound(err)
+ case Forbidden:
+ return IsForbidden(err)
+ default:
+ return false
+ }
+}
+
func isError(err error, precondition func(err error) bool) bool {
if err == nil {
return false
@@ -62,6 +103,16 @@ func isConflictC(err error) bool {
return apierrors.IsConflict(err)
}
+// IsForbidden returns true if the given error is or is caused by a
+// kubernetes ForbiddenError,
+func IsForbidden(err error) bool {
+ return isError(err, isConflictC)
+}
+
+func IsForbiddenC(err error) bool {
+ return apierrors.IsForbidden(err)
+}
+
// IsNotFound returns true if the given error is or is caused by a
// kubernetes NotFoundError,
func IsNotFound(err error) bool {
diff --git a/pkg/util/k8sutil/secrets.go b/pkg/util/k8sutil/secrets.go
index 6dfb68146..a17a5fa74 100644
--- a/pkg/util/k8sutil/secrets.go
+++ b/pkg/util/k8sutil/secrets.go
@@ -216,7 +216,7 @@ func GetTLSKeyfileFromSecret(s *core.Secret) (string, error) {
// CreateTLSKeyfileSecret creates a secret used to store a PEM encoded keyfile
// in the format ArangoDB accepts it for its `--ssl.keyfile` option.
func CreateTLSKeyfileSecret(ctx context.Context, secrets secretv1.ModInterface, secretName string, keyfile string,
- ownerRef *meta.OwnerReference) error {
+ ownerRef *meta.OwnerReference) (*core.Secret, error) {
// Create secret
secret := &core.Secret{
ObjectMeta: meta.ObjectMeta{
@@ -228,11 +228,12 @@ func CreateTLSKeyfileSecret(ctx context.Context, secrets secretv1.ModInterface,
}
// Attach secret to owner
AddOwnerRefToObject(secret, ownerRef)
- if _, err := secrets.Create(ctx, secret, meta.CreateOptions{}); err != nil {
+ if s, err := secrets.Create(ctx, secret, meta.CreateOptions{}); err != nil {
// Failed to create secret
- return kerrors.NewResourceError(err, secret)
+ return nil, kerrors.NewResourceError(err, secret)
+ } else {
+ return s, nil
}
- return nil
}
// ValidateTokenSecret checks that a secret with given name in given namespace