This is an automated email from the ASF dual-hosted git repository.
pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
The following commit(s) were added to refs/heads/main by this push:
new 36b2e679e feat(deployer): deprecate client side apply enforcement
36b2e679e is described below
commit 36b2e679ecd8bd47d6553a41d6a7d91f2bb79689
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Sep 7 07:19:23 2024 +0200
feat(deployer): deprecate client side apply enforcement
---
docs/modules/ROOT/partials/apis/camel-k-crds.adoc | 1 +
docs/modules/traits/pages/deployer.adoc | 3 +-
helm/camel-k/crds/camel-k-crds.yaml | 7 ++
pkg/apis/camel/v1/trait/deployer.go | 1 +
pkg/client/apply.go | 22 ++++-
pkg/install/kamelets.go | 93 ++--------------------
.../camel.apache.org_integrationplatforms.yaml | 2 +
.../camel.apache.org_integrationprofiles.yaml | 2 +
.../crd/bases/camel.apache.org_integrations.yaml | 1 +
.../bases/camel.apache.org_kameletbindings.yaml | 1 +
.../config/crd/bases/camel.apache.org_pipes.yaml | 1 +
pkg/trait/deployer.go | 88 +++++++-------------
12 files changed, 68 insertions(+), 154 deletions(-)
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index 64e5125b5..77c844a21 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -6877,6 +6877,7 @@ bool
|
+Deprecated: won't be able to enforce client side update in the future.
Use server-side apply to update the owned resources (default `true`).
Note that it automatically falls back to client-side patching, if SSA is not
available, e.g., on old Kubernetes clusters.
diff --git a/docs/modules/traits/pages/deployer.adoc
b/docs/modules/traits/pages/deployer.adoc
index a220f1b3c..aa03c1034 100755
--- a/docs/modules/traits/pages/deployer.adoc
+++ b/docs/modules/traits/pages/deployer.adoc
@@ -36,7 +36,8 @@ The following configuration options are available:
| deployer.use-ssa
| bool
-| Use server-side apply to update the owned resources (default `true`).
+| Deprecated: won't be able to enforce client side update in the future.
+Use server-side apply to update the owned resources (default `true`).
Note that it automatically falls back to client-side patching, if SSA is not
available, e.g., on old Kubernetes clusters.
|===
diff --git a/helm/camel-k/crds/camel-k-crds.yaml
b/helm/camel-k/crds/camel-k-crds.yaml
index cfdae68b1..449de8c30 100644
--- a/helm/camel-k/crds/camel-k-crds.yaml
+++ b/helm/camel-k/crds/camel-k-crds.yaml
@@ -4079,6 +4079,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -6220,6 +6221,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -8263,6 +8265,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -10283,6 +10286,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -18343,6 +18347,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -26766,6 +26771,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned
resources (default `true`).
Note that it automatically falls back to
client-side patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -37689,6 +37695,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned
resources (default `true`).
Note that it automatically falls back to
client-side patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git a/pkg/apis/camel/v1/trait/deployer.go
b/pkg/apis/camel/v1/trait/deployer.go
index 70d140ec4..c76dc927a 100644
--- a/pkg/apis/camel/v1/trait/deployer.go
+++ b/pkg/apis/camel/v1/trait/deployer.go
@@ -26,6 +26,7 @@ type DeployerTrait struct {
// Allows to explicitly select the desired deployment kind between
`deployment`, `cron-job` or `knative-service` when creating the resources for
running the integration.
// +kubebuilder:validation:Enum=deployment;cron-job;knative-service
Kind string `property:"kind" json:"kind,omitempty"`
+ // Deprecated: won't be able to enforce client side update in the
future.
// Use server-side apply to update the owned resources (default `true`).
// Note that it automatically falls back to client-side patching, if
SSA is not available, e.g., on old Kubernetes clusters.
UseSSA *bool `property:"use-ssa" json:"useSSA,omitempty"`
diff --git a/pkg/client/apply.go b/pkg/client/apply.go
index 7fe872e84..aa43f5df9 100644
--- a/pkg/client/apply.go
+++ b/pkg/client/apply.go
@@ -19,6 +19,7 @@ package client
import (
"context"
+ "encoding/json"
"errors"
"fmt"
"net/http"
@@ -31,7 +32,6 @@ import (
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
- "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
)
@@ -78,12 +78,17 @@ func (a *ServerOrClientSideApplier) Apply(ctx
context.Context, object ctrl.Objec
return nil
}
-func (a *ServerOrClientSideApplier) serverSideApply(ctx context.Context,
resource runtime.Object) error {
+func (a *ServerOrClientSideApplier) serverSideApply(ctx context.Context,
resource ctrl.Object) error {
target, err := patch.ApplyPatch(resource)
if err != nil {
return err
}
- return a.Client.Patch(ctx, target, ctrl.Apply, ctrl.ForceOwnership,
ctrl.FieldOwner("camel-k-operator"))
+ if err = a.Client.Patch(ctx, target, ctrl.Apply, ctrl.ForceOwnership,
ctrl.FieldOwner("camel-k-operator")); err != nil {
+ return fmt.Errorf("error during apply resource: %s/%s: %w",
resource.GetNamespace(), resource.GetName(), err)
+ }
+
+ // Update the resource with the response returned from the API server
+ return unstructuredToRuntimeObject(target, resource)
}
func (a *ServerOrClientSideApplier) clientSideApply(ctx context.Context,
resource ctrl.Object) error {
@@ -105,7 +110,8 @@ func (a *ServerOrClientSideApplier) clientSideApply(ctx
context.Context, resourc
if err != nil {
return err
} else if len(p) == 0 {
- return nil
+ // Update the resource with the object returned from the API
server
+ return unstructuredToRuntimeObject(object, resource)
}
return a.Client.Patch(ctx, resource,
ctrl.RawPatch(types.MergePatchType, p))
}
@@ -124,3 +130,11 @@ func isIncompatibleServerError(err error) bool {
// Non-StatusError means the error isn't because the server is
incompatible.
return false
}
+
+func unstructuredToRuntimeObject(u *unstructured.Unstructured, obj
ctrl.Object) error {
+ data, err := json.Marshal(u)
+ if err != nil {
+ return err
+ }
+ return json.Unmarshal(data, obj)
+}
diff --git a/pkg/install/kamelets.go b/pkg/install/kamelets.go
index 479eb8463..daaa7894e 100644
--- a/pkg/install/kamelets.go
+++ b/pkg/install/kamelets.go
@@ -19,24 +19,15 @@ package install
import (
"context"
- "encoding/json"
- "errors"
"fmt"
"io/fs"
- "net/http"
"os"
"path/filepath"
"strings"
- "sync"
- "sync/atomic"
"golang.org/x/sync/errgroup"
- k8serrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
- logf "sigs.k8s.io/controller-runtime/pkg/log"
v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
@@ -44,7 +35,7 @@ import (
"github.com/apache/camel-k/v2/pkg/util"
"github.com/apache/camel-k/v2/pkg/util/defaults"
"github.com/apache/camel-k/v2/pkg/util/kubernetes"
- "github.com/apache/camel-k/v2/pkg/util/patch"
+ "github.com/apache/camel-k/v2/pkg/util/log"
)
const (
@@ -52,13 +43,6 @@ const (
defaultKameletDir = "/kamelets/"
)
-var (
- log = logf.Log
-
- hasServerSideApply atomic.Value
- tryServerSideApply sync.Once
-)
-
// KameletCatalog installs the bundled Kamelets into the specified namespace.
func KameletCatalog(ctx context.Context, c client.Client, namespace string)
error {
kameletDir := os.Getenv(kameletDirEnv)
@@ -76,6 +60,7 @@ func KameletCatalog(ctx context.Context, c client.Client,
namespace string) erro
}
g, gCtx := errgroup.WithContext(ctx)
+ applier := c.ServerOrClientSideApplier()
err = filepath.WalkDir(kameletDir, func(p string, f fs.DirEntry, err
error) error {
if err != nil {
@@ -93,38 +78,10 @@ func KameletCatalog(ctx context.Context, c client.Client,
namespace string) erro
if err != nil {
return err
}
- once := false
- tryServerSideApply.Do(func() {
- once = true
- if err = serverSideApply(gCtx, c, kamelet); err
!= nil {
- if isIncompatibleServerError(err) {
- log.Info("Fallback to
client-side apply for installing bundled Kamelets")
- hasServerSideApply.Store(false)
- err = nil
- } else {
- // Unexpected error occurred
- err = fmt.Errorf("unexpected
error occurred whilst validating kamelet: %w", err)
- log.Error(err, "Error occurred
whilst loading kamelets")
- }
- } else {
- hasServerSideApply.Store(true)
- }
- })
- if err != nil {
- return err
- }
- v := hasServerSideApply.Load()
- var bundleKamError error
- if vb, ok := v.(bool); ok && vb {
- if !once {
- bundleKamError = serverSideApply(gCtx,
c, kamelet)
- }
- } else {
- bundleKamError = clientSideApply(gCtx, c,
kamelet)
- }
+ err = applier.Apply(gCtx, kamelet)
// We only log the error. If we returned the error, the
creation of the ITP would have stopped
- if bundleKamError != nil {
- log.Error(bundleKamError, "Error occurred
whilst applying bundled kamelet")
+ if err != nil {
+ log.Error(err, "Error occurred whilst applying
bundled kamelet")
}
return nil
})
@@ -137,43 +94,6 @@ func KameletCatalog(ctx context.Context, c client.Client,
namespace string) erro
return g.Wait()
}
-func serverSideApply(ctx context.Context, c client.Client, resource
runtime.Object) error {
- target, err := patch.ApplyPatch(resource)
- if err != nil {
- return err
- }
- return c.Patch(ctx, target, ctrl.Apply, ctrl.ForceOwnership,
ctrl.FieldOwner("camel-k-operator"))
-}
-
-func clientSideApply(ctx context.Context, c client.Client, resource
ctrl.Object) error {
- if err := c.Create(ctx, resource); err == nil {
- return nil
- } else if !k8serrors.IsAlreadyExists(err) {
- return fmt.Errorf("error during create resource: %s/%s: %w",
resource.GetNamespace(), resource.GetName(), err)
- }
- // Directly use the serialized resource as JSON merge patch since it's
prescriptive
- p, err := json.Marshal(resource)
- if err != nil {
- return err
- }
- return c.Patch(ctx, resource, ctrl.RawPatch(types.MergePatchType, p))
-}
-
-func isIncompatibleServerError(err error) bool {
- // First simpler check for older servers (i.e. OpenShift 3.11)
- if strings.Contains(err.Error(), "415: Unsupported Media Type") {
- return true
- }
- // 415: Unsupported media type means we're talking to a server which
doesn't
- // support server-side apply.
- var serr *k8serrors.StatusError
- if errors.As(err, &serr) {
- return serr.Status().Code == http.StatusUnsupportedMediaType
- }
- // Non-StatusError means the error isn't because the server is
incompatible.
- return false
-}
-
func loadKamelet(path string, namespace string) (ctrl.Object, error) {
content, err := util.ReadFile(path)
if err != nil {
@@ -212,8 +132,5 @@ func loadKamelet(path string, namespace string)
(ctrl.Object, error) {
// KameletViewerRole installs the role that allows any user ro access kamelets
in the global namespace.
func KameletViewerRole(ctx context.Context, c client.Client, namespace string)
error {
- if err := Resource(ctx, c, namespace, true, IdentityResourceCustomizer,
"/resources/viewer/user-global-kamelet-viewer-role.yaml"); err != nil {
- return err
- }
return Resource(ctx, c, namespace, true, IdentityResourceCustomizer,
"/resources/viewer/user-global-kamelet-viewer-role-binding.yaml")
}
diff --git
a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
index 01bd35632..68c15d04c 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -924,6 +924,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -3065,6 +3066,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git
a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
index 5987791c8..1e9642cb4 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
@@ -792,6 +792,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
@@ -2812,6 +2813,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
index e6fa68441..20be24560 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
@@ -6812,6 +6812,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned resources
(default `true`).
Note that it automatically falls back to client-side
patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git
a/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml
index 2660bd748..74341212a 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_kameletbindings.yaml
@@ -6879,6 +6879,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned
resources (default `true`).
Note that it automatically falls back to
client-side patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
index 83d2f95d6..713ea9187 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
@@ -6877,6 +6877,7 @@ spec:
type: string
useSSA:
description: |-
+ Deprecated: won't be able to enforce client side
update in the future.
Use server-side apply to update the owned
resources (default `true`).
Note that it automatically falls back to
client-side patching, if SSA is not available, e.g., on old Kubernetes clusters.
type: boolean
diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go
index a308663a1..65051e53a 100644
--- a/pkg/trait/deployer.go
+++ b/pkg/trait/deployer.go
@@ -18,21 +18,18 @@ limitations under the License.
package trait
import (
- "encoding/json"
- "errors"
"fmt"
- "net/http"
- "strings"
+ v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+ traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
+ "github.com/apache/camel-k/v2/pkg/util/patch"
+ corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/apimachinery/pkg/util/json"
"k8s.io/utils/ptr"
-
ctrl "sigs.k8s.io/controller-runtime/pkg/client"
-
- traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
- "github.com/apache/camel-k/v2/pkg/util/patch"
)
const (
@@ -47,8 +44,6 @@ type deployerTrait struct {
var _ ControllerStrategySelector = &deployerTrait{}
-var hasServerSideApply = true
-
func newDeployerTrait() Trait {
return &deployerTrait{
BasePlatformTrait: NewBasePlatformTrait(deployerTraitID,
deployerTraitOrder),
@@ -56,35 +51,34 @@ func newDeployerTrait() Trait {
}
func (t *deployerTrait) Configure(e *Environment) (bool, *TraitCondition,
error) {
- return e.Integration != nil, nil, nil
+ var condition *TraitCondition
+ //nolint: staticcheck
+ if !ptr.Deref(t.UseSSA, true) {
+ condition = NewIntegrationCondition(
+ "Deployer",
+ v1.IntegrationConditionTraitInfo,
+ corev1.ConditionTrue,
+ traitConfigurationReason,
+ "The use-ssa parameter is deprecated and may be removed
in future releases.",
+ )
+ }
+ return e.Integration != nil, condition, nil
}
func (t *deployerTrait) Apply(e *Environment) error {
// Register a post action that patches the resources generated by the
traits
e.PostActions = append(e.PostActions, func(env *Environment) error {
+ applier := e.Client.ServerOrClientSideApplier()
for _, resource := range env.Resources.Items() {
- // We assume that server-side apply is enabled by
default.
- // It is currently convoluted to check proactively
whether server-side apply
- // is enabled. This is possible to fetch the OpenAPI
endpoint, which returns
- // the entire server API document, then lookup the
resource PATCH endpoint, and
- // check its list of accepted MIME types.
- // As a simpler solution, we fall back to client-side
apply at the first
- // 415 error, and assume server-side apply is not
available globally.
- if hasServerSideApply && ptr.Deref(t.UseSSA, true) {
- err := t.serverSideApply(env, resource)
- switch {
- case err == nil:
- continue
- case isIncompatibleServerError(err):
- t.L.Info("Fallback to client-side apply
to patch resources")
- hasServerSideApply = false
- default:
- // Keep server-side apply unless server
is incompatible with it
+ //nolint: staticcheck
+ if ptr.Deref(t.UseSSA, true) {
+ if err := applier.Apply(e.Ctx, resource); err
!= nil {
+ return err
+ }
+ } else {
+ if err := t.clientSideApply(env, resource); err
!= nil {
return err
}
- }
- if err := t.clientSideApply(env, resource); err != nil {
- return err
}
}
return nil
@@ -93,19 +87,7 @@ func (t *deployerTrait) Apply(e *Environment) error {
return nil
}
-func (t *deployerTrait) serverSideApply(env *Environment, resource
ctrl.Object) error {
- target, err := patch.ApplyPatch(resource)
- if err != nil {
- return err
- }
- err = env.Client.Patch(env.Ctx, target, ctrl.Apply,
ctrl.ForceOwnership, ctrl.FieldOwner("camel-k-operator"))
- if err != nil {
- return fmt.Errorf("error during apply resource: %s/%s: %w",
resource.GetNamespace(), resource.GetName(), err)
- }
- // Update the resource with the response returned from the API server
- return t.unstructuredToRuntimeObject(target, resource)
-}
-
+// Deprecated: use ServerOrClientSideApplier() instead.
func (t *deployerTrait) clientSideApply(env *Environment, resource
ctrl.Object) error {
err := env.Client.Create(env.Ctx, resource)
if err == nil {
@@ -135,6 +117,7 @@ func (t *deployerTrait) clientSideApply(env *Environment,
resource ctrl.Object)
return nil
}
+// Deprecated: use ServerOrClientSideApplier() instead.
func (t *deployerTrait) unstructuredToRuntimeObject(u
*unstructured.Unstructured, obj ctrl.Object) error {
data, err := json.Marshal(u)
if err != nil {
@@ -143,23 +126,6 @@ func (t *deployerTrait) unstructuredToRuntimeObject(u
*unstructured.Unstructured
return json.Unmarshal(data, obj)
}
-func isIncompatibleServerError(err error) bool {
- // First simpler check for older servers (i.e. OpenShift 3.11)
- if strings.Contains(err.Error(), "415: Unsupported Media Type") {
- return true
- }
-
- // 415: Unsupported media type means we're talking to a server which
doesn't
- // support server-side apply.
- var serr *k8serrors.StatusError
- if errors.As(err, &serr) {
- return serr.Status().Code == http.StatusUnsupportedMediaType
- }
-
- // Non-StatusError means the error isn't because the server is
incompatible.
- return false
-}
-
func (t *deployerTrait) SelectControllerStrategy(e *Environment)
(*ControllerStrategy, error) {
if t.Kind != "" {
strategy := ControllerStrategy(t.Kind)