Skip to content

Commit

Permalink
Fix #2361: allow multiline properties by correctly encoding them
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaferraro committed Jun 9, 2021
1 parent 2b10cea commit 41e7ac5
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
kind: KameletBinding
apiVersion: camel.apache.org/v1alpha1
metadata:
name: properties-binding
spec:
source:
ref:
apiVersion: camel.apache.org/v1alpha1
kind: Kamelet
name: timer-source
properties:
message: |
{
"content": "thecontent",
"key2": "val2"
}
contentType: "application/json"
sink:
uri: http://probe-service/events
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Feature: Ensure that Kamelets support multiline configuration

Background:
Given Disable auto removal of Kamelet resources
Given Disable auto removal of Kubernetes resources
Given Camel-K resource polling configuration
| maxAttempts | 60 |
| delayBetweenAttempts | 3000 |

Scenario: Wait for binding to start
Given create Kubernetes service probe-service with target port 8080
Then Camel-K integration properties-binding should be running

Scenario: Verify binding
Given HTTP server "probe-service"
And HTTP server timeout is 300000 ms
Then expect HTTP request body
"""
{
"content": "thecontent",
"key2": "val2"
}
"""
And expect HTTP request header: Content-Type="application/json;charset=UTF-8"
And receive POST /events
And delete KameletBinding properties-binding
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# ---------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
# ---------------------------------------------------------------------------

apiVersion: camel.apache.org/v1alpha1
kind: Kamelet
metadata:
name: timer-source
annotations:
camel.apache.org/kamelet.support.level: "Preview"
camel.apache.org/catalog.version: "main-SNAPSHOT"
camel.apache.org/kamelet.icon: data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gU3ZnIFZlY3RvciBJY29ucyA6IGh0dHA6Ly93d3cub25saW5ld2ViZm9udHMuY29tL2ljb24gLS0+DQo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTAwMCAxMDAwIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMDAwIDEwMDAiIHhtbDpzcGFjZT0icHJlc2VydmUiPg0KPG1ldGFkYXRhPiBTdmcgVmVjdG9yIEljb25zIDogaHR0cDovL3d3dy5vbmxpbmV3ZWJmb250cy5jb20vaWNvbiA8L21ldGFkYXRhPg0KPGc+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsNTExLjAwMDAwMCkgc2NhbGUoMC4xMDAwMDAsLTAuMTAwMDAwKSI+PHBhdGggZD0iTTM4ODguMSw0Nzc0Ljl2LTIzNS4xaDQxMS40aDQxNC4zbC04LjgtMzI5LjFsLTguOC0zMzJsLTExNy41LTguOGMtMjI5LjItMTQuNy02MjAtOTkuOS05MjUuNi0xOTYuOUMyMjU3LjQsMzIyMC42LDExNjcuMiwyMDY1LjgsODAyLjksNjQ5LjZjLTUxMS4zLTE5ODYuMywzODQuOS00MDAyLDIyMDYuNy00OTY1LjhjMzAyLjYtMTYxLjYsNzU4LjEtMzIwLjIsMTE1NC44LTQwNS41YzQyNi4xLTkxLjEsMTI1MS43LTkxLjEsMTY4MC43LDBjMTc2OC45LDM4MiwzMDQ0LjEsMTY1Ny4yLDM0MjYuMSwzNDI2LjFjOTEuMSw0MjYuMSw5MS4xLDEyNTQuNiwwLDE2NzcuOGMtNDIwLjIsMTk0Mi4yLTE5MzYuNCwzMzAyLjYtMzg5MC4zLDM0OTYuNmwtMTk5LjgsMjAuNnYzMjAuM3YzMjAuM2g0MTEuNGg0MTEuNHYyMzUuMVY1MDEwSDQ5NDUuOUgzODg4LjFWNDc3NC45eiBNNTc1My45LDMzNDkuOWM3NzguNy0xNjEuNiwxNDE5LjItNTA4LjMsMTk4My40LTEwNzIuNWM1NjQuMi01NjEuMiw4ODcuNC0xMTU3LjcsMTA2MC43LTE5NDIuMmM5OS45LTQzNy44LDk5LjktMTE0MywzLTE1ODAuOEM4NTYzLTIzMDYuNCw3OTY2LjUtMzE1OC41LDcwNDMuOS0zNzUyYy0zMzUtMjE0LjUtNzg3LjUtMzk2LjctMTI0OC44LTQ5OS41Yy00MzcuOC05Ny0xMTQzLTk3LTE1ODAuOCwyLjljLTc4NC41LDE3My4zLTEzODEsNDk2LjYtMTk0Mi4yLDEwNjAuN2MtNTcwLDU2Ny4xLTkwNy45LDExOTguOC0xMDc4LjQsMTk5OC4xYy03My41LDM0Ni43LTczLjUsMTEyMi40LDAsMTQ2OS4yYzE3MC40LDc5OS4yLDUwOC4zLDE0MzEsMTA3OC40LDE5OThDMjg5NSwyOTAwLjMsMzYzOC40LDMyNzMuNSw0NDkzLjQsMzM5MUM0Nzc4LjQsMzQzMi4xLDU0NzQuOCwzNDA4LjYsNTc1My45LDMzNDkuOXoiLz48cGF0aCBkPSJNNDcxMC44LDEzNzUuM1YyMDUuOUw0NTUyLjIsNjcuOGMtMzE3LjMtMjc5LjEtMzQwLjgtNjc4LjctNTUuOC05OTMuMWMyODcuOS0zMjAuMyw2OTMuNC0zMTcuMywxMDEzLjcsNS45bDE3MC40LDE3MC40aDEwNDMuMWgxMDQzLjFWLTUxNFYtMjc5SDY3MjkuNUg1NjkyLjJsLTQ5LjksMTE0LjZjLTU4LjgsMTMyLjItMjUyLjcsMzE3LjMtMzc2LjEsMzYxLjRsLTg1LjIsMjkuNHYxMTU3Ljd2MTE1Ny43aC0yMzUuMWgtMjM1LjFWMTM3NS4zeiBNNTE2Ni4zLTI5My42YzE0Ni45LTE0NCw0NC4xLTM5Ni43LTE2MS42LTM5Ni43Yy01NS44LDAtMTE3LjUsMjYuNC0xNjEuNiw3My40Yy00Nyw0NC4xLTczLjUsMTA1LjgtNzMuNSwxNjEuNnMyNi40LDExNy41LDczLjUsMTYxLjZjNDQuMSw0NywxMDUuOCw3My41LDE2MS42LDczLjVDNTA2MC41LTIyMC4yLDUxMjIuMi0yNDYuNyw1MTY2LjMtMjkzLjZ6Ii8+PC9nPjwvZz4NCjwvc3ZnPg==
camel.apache.org/provider: "Apache Software Foundation"
camel.apache.org/kamelet.group: "Timer"
labels:
camel.apache.org/kamelet.type: source
camel.apache.org/kamelet.verified: "true"
spec:
definition:
title: Timer Source
description: Produces periodic events with a custom payload.
required:
- message
type: object
properties:
period:
title: Period
description: The interval between two events in milliseconds
type: integer
default: 1000
message:
title: Message
description: The message to generate
type: string
example: hello world
contentType:
title: Content Type
description: The content type of the message being generated
type: string
default: text/plain
dependencies:
- "camel:core"
- "camel:timer"
- "camel:kamelet"
flow:
from:
uri: timer:tick
parameters:
period: "{{period}}"
steps:
- set-body:
constant: "{{message}}"
- set-header:
name: "Content-Type"
constant: "{{contentType}}"
- to: kamelet:sink
27 changes: 27 additions & 0 deletions e2e/yaks/common/kamelet-binding-property-encoding/yaks-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# ---------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
# ---------------------------------------------------------------------------

config:
namespace:
temporary: true
pre:
- name: installation
run: |
kamel install -n $YAKS_NAMESPACE
kubectl apply -f timer-source.kamelet.yaml -n $YAKS_NAMESPACE
kubectl apply -f properties-binding.yaml -n $YAKS_NAMESPACE
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/google/go-github/v32 v32.1.0
github.com/google/uuid v1.2.0
github.com/jpillora/backoff v1.0.0
github.com/magiconair/properties v1.8.1
github.com/magiconair/properties v1.8.5
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.1.2
github.com/onsi/gomega v1.10.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,8 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
Expand Down
33 changes: 25 additions & 8 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
package cmd

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -584,9 +585,13 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
return nil, err
}
for _, k := range props.Keys() {
v, ok := props.Get(k)
_, ok := props.Get(k)
if ok {
o.Traits = append(o.Traits, fmt.Sprintf("builder.properties=%s", escapePropertyFileItem(k)+"="+escapePropertyFileItem(v)))
entry, err := toPropertyEntry(props, k)
if err != nil {
return nil, err
}
o.Traits = append(o.Traits, fmt.Sprintf("builder.properties=%s", entry))
} else {
return nil, err
}
Expand Down Expand Up @@ -788,10 +793,13 @@ func isLocalAndFileExists(fileName string) (bool, error) {

func addIntegrationProperties(props *properties.Properties, spec *v1.IntegrationSpec) error {
for _, k := range props.Keys() {
v, _ := props.Get(k)
entry, err := toPropertyEntry(props, k)
if err != nil {
return err
}
spec.AddConfiguration(
"property",
escapePropertyFileItem(k)+"="+escapePropertyFileItem(v),
entry,
)
}
return nil
Expand All @@ -809,10 +817,19 @@ func loadPropertyFile(fileName string) (*properties.Properties, error) {
return p, nil
}

func escapePropertyFileItem(item string) string {
item = strings.ReplaceAll(item, `=`, `\=`)
item = strings.ReplaceAll(item, `:`, `\:`)
return item
func toPropertyEntry(props *properties.Properties, key string) (string, error) {
value, _ := props.Get(key)
p := properties.NewProperties()
p.DisableExpansion = true
if _, _, err := p.Set(key, value); err != nil {
return "", err
}
buf := new(bytes.Buffer)
if _, err := p.Write(buf, properties.UTF8); err != nil {
return "", err
}
pair := strings.TrimSuffix(buf.String(), "\n")
return pair, nil
}

func resolvePodTemplate(ctx context.Context, templateSrc string, spec *v1.IntegrationSpec) (err error) {
Expand Down
18 changes: 10 additions & 8 deletions pkg/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,12 @@ func TestRunPropertyFileFlagMissingFileExtension(t *testing.T) {

const TestPropertyFileContent = `
a=b
c\=d=e
d=c\=e
# There's an issue in the properties lib: https://github.com/magiconair/properties/issues/59
# so the following cases have been commented. Btw, we don't use equal sign in keys
#c\=d=e
#d=c\=e
#ignore=me
f=g\:h
f=g:h
`

func TestAddPropertyFile(t *testing.T) {
Expand All @@ -265,11 +267,11 @@ func TestAddPropertyFile(t *testing.T) {
properties, err := extractProperties("file:" + tmpFile.Name())
assert.Nil(t, err)
assert.Nil(t, addIntegrationProperties(properties, &spec))
assert.Equal(t, 4, len(spec.Configuration))
assert.Equal(t, `a=b`, spec.Configuration[0].Value)
assert.Equal(t, `c\=d=e`, spec.Configuration[1].Value)
assert.Equal(t, `d=c\=e`, spec.Configuration[2].Value)
assert.Equal(t, `f=g\:h`, spec.Configuration[3].Value)
assert.Equal(t, 2, len(spec.Configuration))
assert.Equal(t, `a = b`, spec.Configuration[0].Value)
//assert.Equal(t, `c\=d=e`, spec.Configuration[1].Value)
//assert.Equal(t, `d=c\=e`, spec.Configuration[2].Value)
assert.Equal(t, `f = g:h`, spec.Configuration[1].Value)
}

func TestRunPropertyFileFlag(t *testing.T) {
Expand Down
24 changes: 22 additions & 2 deletions pkg/controller/kameletbinding/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ limitations under the License.
package kameletbinding

import (
"bytes"
"context"
"encoding/json"
"fmt"
"sort"
"strings"

v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
Expand All @@ -31,6 +32,7 @@ import (
"github.com/apache/camel-k/pkg/util/bindings"
"github.com/apache/camel-k/pkg/util/knative"
"github.com/apache/camel-k/pkg/util/kubernetes"
"github.com/magiconair/properties"
"github.com/pkg/errors"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -127,7 +129,11 @@ func createIntegrationFor(ctx context.Context, c client.Client, kameletbinding *
it.Spec.Traits[k] = v
}
for k, v := range b.ApplicationProperties {
propList = append(propList, fmt.Sprintf("%s=%s", k, v))
entry, err := toPropertyEntry(k, v)
if err != nil {
return nil, err
}
propList = append(propList, entry)
}
}

Expand Down Expand Up @@ -192,3 +198,17 @@ func determineProfile(ctx context.Context, c client.Client, binding *v1alpha1.Ka
}
return v1.DefaultTraitProfile, nil
}

func toPropertyEntry(key, value string) (string, error) {
p := properties.NewProperties()
p.DisableExpansion = true
if _, _, err := p.Set(key, value); err != nil {
return "", err
}
buf := new(bytes.Buffer)
if _, err := p.Write(buf, properties.UTF8); err != nil {
return "", err
}
pair := strings.TrimSuffix(buf.String(), "\n")
return pair, nil
}
2 changes: 1 addition & 1 deletion pkg/trait/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (t *builderTrait) builderTask(e *Environment) (*v1.BuilderTask, error) {
// User provided Maven properties
if t.Properties != nil {
for _, v := range t.Properties {
split := strings.Split(v, "=")
split := strings.SplitN(v, "=", 2)
if len(split) != 2 {
return nil, fmt.Errorf("maven property must have key=value format, it was %v", v)
}
Expand Down
12 changes: 9 additions & 3 deletions pkg/trait/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,9 @@ func (t *containerTrait) configureContainer(e *Environment) error {
for _, envVar := range e.EnvVars {
envvar.SetVar(&container.Env, envVar)
}
if props := e.computeApplicationProperties(); props != nil {
if props, err := e.computeApplicationProperties(); err != nil {
return err
} else if props != nil {
e.Resources.Add(props)
}

Expand Down Expand Up @@ -291,7 +293,9 @@ func (t *containerTrait) configureContainer(e *Environment) error {
envvar.SetVar(&container.Env, env)
}
}
if props := e.computeApplicationProperties(); props != nil {
if props, err := e.computeApplicationProperties(); err != nil {
return err
} else if props != nil {
e.Resources.Add(props)
}

Expand All @@ -318,7 +322,9 @@ func (t *containerTrait) configureContainer(e *Environment) error {
for _, envVar := range e.EnvVars {
envvar.SetVar(&container.Env, envVar)
}
if props := e.computeApplicationProperties(); props != nil {
if props, err := e.computeApplicationProperties(); err != nil {
return err
} else if props != nil {
e.Resources.Add(props)
}

Expand Down
Loading

0 comments on commit 41e7ac5

Please sign in to comment.