You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by as...@apache.org on 2021/02/18 16:11:34 UTC
[camel-k] 03/03: chore: Fallback to client-side apply if necessary
This is an automated email from the ASF dual-hosted git repository.
astefanutti pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 70f7946cb5a4cdc224c7c25e49b232867a4f815e
Author: Antonin Stefanutti <an...@stefanutti.fr>
AuthorDate: Wed Feb 17 18:31:10 2021 +0100
chore: Fallback to client-side apply if necessary
---
pkg/trait/deployer.go | 82 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 76 insertions(+), 6 deletions(-)
diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go
index d9fa584..a9a11d7 100644
--- a/pkg/trait/deployer.go
+++ b/pkg/trait/deployer.go
@@ -18,8 +18,14 @@ limitations under the License.
package trait
import (
+ "net/http"
+
"github.com/pkg/errors"
+ k8serrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/types"
+
"sigs.k8s.io/controller-runtime/pkg/client"
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -38,6 +44,8 @@ type deployerTrait struct {
var _ ControllerStrategySelector = &deployerTrait{}
+var hasServerSideApply = true
+
func newDeployerTrait() Trait {
return &deployerTrait{
BaseTrait: NewBaseTrait("deployer", 900),
@@ -75,13 +83,23 @@ 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 {
for _, resource := range env.Resources.Items() {
- target, err := patch.PositiveApplyPatch(resource)
- if err != nil {
- return err
+ // We assume that server-side apply is enabled by default.
+ // It is currently convoluted to check pro-actively 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 fallback to client-side apply at the first
+ // 415 error, and assume server-side apply is not available globally.
+ if hasServerSideApply {
+ if err := t.serverSideApply(env, resource); err == nil {
+ continue
+ } else if isIncompatibleServerError(err) {
+ t.L.Info("Fallback to client-side apply to patch resources")
+ hasServerSideApply = false
+ }
}
- err = env.Client.Patch(env.C, target, client.Apply, client.ForceOwnership, client.FieldOwner("camel-k-operator"))
- if err != nil {
- return errors.Wrapf(err, "error during apply resource: %v", resource)
+ if err := t.clientSideApply(env, resource); err != nil {
+ return err
}
}
return nil
@@ -91,6 +109,58 @@ func (t *deployerTrait) Apply(e *Environment) error {
return nil
}
+func (t *deployerTrait) serverSideApply(env *Environment, resource runtime.Object) error {
+ target, err := patch.PositiveApplyPatch(resource)
+ if err != nil {
+ return err
+ }
+ err = env.Client.Patch(env.C, target, client.Apply, client.ForceOwnership, client.FieldOwner("camel-k-operator"))
+ if err != nil {
+ return errors.Wrapf(err, "error during apply resource: %v", resource)
+ }
+ return nil
+}
+
+func (t *deployerTrait) clientSideApply(env *Environment, resource runtime.Object) error {
+ err := env.Client.Create(env.C, resource)
+ if err == nil {
+ return nil
+ } else if !k8serrors.IsAlreadyExists(err) {
+ return errors.Wrapf(err, "error during create resource: %v", resource)
+ }
+ key, err := client.ObjectKeyFromObject(resource)
+ if err != nil {
+ return err
+ }
+ object := resource.DeepCopyObject()
+ err = env.Client.Get(env.C, key, object)
+ if err != nil {
+ return err
+ }
+ p, err := patch.PositiveMergePatch(object, resource)
+ if err != nil {
+ return err
+ } else if len(p) == 0 {
+ // Avoid triggering a patch request for nothing
+ return nil
+ }
+ err = env.Client.Patch(env.C, resource, client.RawPatch(types.MergePatchType, p))
+ if err != nil {
+ return errors.Wrapf(err, "error during patch resource: %v", resource)
+ }
+ return nil
+}
+
+func isIncompatibleServerError(err error) bool {
+ // 415: Unsupported media type means we're talking to a server which doesn't
+ // support server-side apply.
+ if _, ok := err.(*k8serrors.StatusError); !ok {
+ // Non-StatusError means the error isn't because the server is incompatible.
+ return false
+ }
+ return err.(*k8serrors.StatusError).Status().Code == http.StatusUnsupportedMediaType
+}
+
func (t *deployerTrait) SelectControllerStrategy(e *Environment) (*ControllerStrategy, error) {
if t.Enabled != nil && !*t.Enabled {
return nil, nil