Skip to content

Commit

Permalink
feat(deployer): deprecate client side apply enforcement
Browse files Browse the repository at this point in the history
  • Loading branch information
squakez committed Sep 7, 2024
1 parent 57f7e5b commit 36b2e67
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 154 deletions.
1 change: 1 addition & 0 deletions docs/modules/ROOT/partials/apis/camel-k-crds.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion docs/modules/traits/pages/deployer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.

|===
Expand Down
7 changes: 7 additions & 0 deletions helm/camel-k/crds/camel-k-crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/camel/v1/trait/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
22 changes: 18 additions & 4 deletions pkg/client/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package client

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
Expand All @@ -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"
)
Expand Down Expand Up @@ -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 {
Expand All @@ -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))
}
Expand All @@ -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)
}
93 changes: 5 additions & 88 deletions pkg/install/kamelets.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,46 +19,30 @@ 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"

"github.com/apache/camel-k/v2/pkg/client"
"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 (
kameletDirEnv = "KAMELET_CATALOG_DIR"
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)
Expand All @@ -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 {
Expand All @@ -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
})
Expand All @@ -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 {
Expand Down Expand Up @@ -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")
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 36b2e67

Please sign in to comment.