Skip to content

Commit

Permalink
feat(trait): leverage ConfigMap binary data for resources
Browse files Browse the repository at this point in the history
* Any binary data will be using BinaryData ConfiMap instead of Data. We let the cluster to encode/decode the resource
* Any text resource will be still using the Data (plain) ConfigMap
* The `compression` feature can be run both on binary and text resources, providing a base64 encoded file.
* Added unit test to check all the possible scenarios

Close apache#1946, close apache#1881
  • Loading branch information
squakez committed Jan 29, 2021
1 parent 5817d05 commit de1b258
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 86 deletions.
2 changes: 2 additions & 0 deletions pkg/apis/camel/v1/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,10 @@ const (
type DataSpec struct {
Name string `json:"name,omitempty"`
Content string `json:"content,omitempty"`
RawContent []byte `json:"rawContent,omitempty"`
ContentRef string `json:"contentRef,omitempty"`
ContentKey string `json:"contentKey,omitempty"`
ContentType string `json:"contentType,omitempty"`
Compression bool `json:"compression,omitempty"`
}

Expand Down
46 changes: 36 additions & 10 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,23 +495,20 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
}

for _, resource := range o.Resources {
data, compressed, err := loadContent(resource, o.Compression, o.CompressBinary)
rawData, contentType, err := loadRawContent(resource)
if err != nil {
return nil, err
}

integration.Spec.AddResources(v1.ResourceSpec{
DataSpec: v1.DataSpec{
Name: path.Base(resource),
Content: data,
Compression: compressed,
},
Type: v1.ResourceTypeData,
})
resourceSpec, err := binaryOrTextResource(path.Base(resource), rawData, contentType, o.Compression)
if err != nil {
return nil, err
}
integration.Spec.AddResources(resourceSpec)
}

for _, resource := range o.OpenAPIs {
data, compressed, err := loadContent(resource, o.Compression, o.CompressBinary)
data, _, compressed, err := loadTextContent(resource, o.Compression)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -623,6 +620,35 @@ func (o *runCmdOptions) updateIntegrationCode(c client.Client, sources []string,
return &integration, nil
}

func binaryOrTextResource(fileName string, data []byte, contentType string, base64Compression bool) (v1.ResourceSpec, error) {
resourceSpec := v1.ResourceSpec{
DataSpec: v1.DataSpec{
Name: fileName,
ContentKey: fileName,
ContentType: contentType,
Compression: false,
},
Type: v1.ResourceTypeData,
}

if !base64Compression && isBinary(contentType) {
resourceSpec.RawContent = data
return resourceSpec, nil
}
// either is a text resource or base64 compression is enabled
if base64Compression {
content, err := compressToString(data)
if err != nil {
return resourceSpec, err
}
resourceSpec.Content = content
resourceSpec.Compression = true
} else {
resourceSpec.Content = string(data)
}
return resourceSpec, nil
}

func (o *runCmdOptions) GetIntegrationName(sources []string) string {
name := ""
if o.IntegrationName != "" {
Expand Down
44 changes: 44 additions & 0 deletions pkg/cmd/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,3 +477,47 @@ func TestRunWithSavedValues(t *testing.T) {
assert.Nil(t, err)
assert.Len(t, runCmdOptions.Sources, 2)
}*/

func TestRunBinaryResource(t *testing.T) {
binaryResourceSpec, err := binaryOrTextResource("file.ext", []byte{1, 2, 3, 4}, "application/octet-stream", false)
assert.Nil(t, err)
assert.Equal(t, "", binaryResourceSpec.Content)
assert.NotNil(t, binaryResourceSpec.RawContent)
assert.Equal(t, "file.ext", binaryResourceSpec.Name)
assert.Equal(t, "application/octet-stream", binaryResourceSpec.ContentType)
assert.False(t, binaryResourceSpec.Compression)
}

func TestRunBinaryCompressedResource(t *testing.T) {
data := []byte{1, 2, 3, 4}
base64Compressed, _ := compressToString(data)
binaryResourceSpec, err := binaryOrTextResource("file.ext", data, "application/octet-stream", true)
assert.Nil(t, err)
assert.Equal(t, base64Compressed, binaryResourceSpec.Content)
assert.Nil(t, binaryResourceSpec.RawContent)
assert.Equal(t, "file.ext", binaryResourceSpec.Name)
assert.Equal(t, "application/octet-stream", binaryResourceSpec.ContentType)
assert.True(t, binaryResourceSpec.Compression)
}

func TestRunTextResource(t *testing.T) {
textResourceSpec, err := binaryOrTextResource("file.ext", []byte("hello world"), "text/plain", false)
assert.Nil(t, err)
assert.Equal(t, "hello world", textResourceSpec.Content)
assert.Nil(t, textResourceSpec.RawContent)
assert.Equal(t, "file.ext", textResourceSpec.Name)
assert.Equal(t, "text/plain", textResourceSpec.ContentType)
assert.False(t, textResourceSpec.Compression)
}

func TestRunTextCompressedResource(t *testing.T) {
data := []byte("hello horld")
base64Compressed, _ := compressToString(data)
textResourceSpec, err := binaryOrTextResource("file.ext", []byte("hello horld"), "text/plain", true)
assert.Nil(t, err)
assert.Equal(t, base64Compressed, textResourceSpec.Content)
assert.Nil(t, textResourceSpec.RawContent)
assert.Equal(t, "file.ext", textResourceSpec.Name)
assert.Equal(t, "text/plain", textResourceSpec.ContentType)
assert.True(t, textResourceSpec.Compression)
}
37 changes: 23 additions & 14 deletions pkg/cmd/util_content.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"strings"
)

func loadContent(source string, compress bool, compressBinary bool) (string, bool, error) {
func loadRawContent(source string) ([]byte, string, error) {
var content []byte
var err error

Expand All @@ -35,7 +35,7 @@ func loadContent(source string, compress bool, compressBinary bool) (string, boo
} else {
u, err := url.Parse(source)
if err != nil {
return "", false, err
return nil, "", err
}

switch u.Scheme {
Expand All @@ -46,27 +46,36 @@ func loadContent(source string, compress bool, compressBinary bool) (string, boo
case "https":
content, err = loadContentHTTP(u)
default:
return "", false, fmt.Errorf("unsupported scheme %s", u.Scheme)
return nil, "", fmt.Errorf("unsupported scheme %s", u.Scheme)
}
}

if err != nil {
return "", false, err
return nil, "", err
}
doCompress := compress
if !doCompress && compressBinary {
contentType := http.DetectContentType(content)
if strings.HasPrefix(contentType, "application/octet-stream") {
doCompress = true
}

contentType := http.DetectContentType(content)
return content, contentType, nil
}

func isBinary(contentType string) bool {
// According the http.DetectContentType method
// also json and other "text" application mime types would be reported as text
return !strings.HasPrefix(contentType, "text")
}

func loadTextContent(source string, base64Compression bool) (string, string, bool, error) {
content, contentType, err := loadRawContent(source)
if err != nil {
return "", "", false, err
}

if doCompress {
answer, err := compressToString(content)
return answer, true, err
if base64Compression {
base64Compressed, err := compressToString(content)
return base64Compressed, contentType, true, err
}

return string(content), false, nil
return string(content), contentType, false, nil
}

func loadContentHTTP(u *url.URL) ([]byte, error) {
Expand Down
99 changes: 99 additions & 0 deletions pkg/cmd/util_content_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
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.
*/

package cmd

import (
"io/ioutil"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func TestRawContentFileMissing(t *testing.T) {
_, _, err := loadRawContent("dsadas")
assert.NotNil(t, err)
}

func TestRawBinaryContentType(t *testing.T) {
var tmpFile *os.File
var err error
if tmpFile, err = ioutil.TempFile("", "camel-k-*.json"); err != nil {
t.Error(err)
}
assert.Nil(t, tmpFile.Close())
assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte{1, 2, 3, 4, 5, 6}, 0644))

data, contentType, err := loadRawContent(tmpFile.Name())
assert.Nil(t, err)
assert.Equal(t, []byte{1, 2, 3, 4, 5, 6}, data)
assert.True(t, isBinary(contentType))
}

func TestRawApplicationContentType(t *testing.T) {
var tmpFile *os.File
var err error
if tmpFile, err = ioutil.TempFile("", "camel-k-*.json"); err != nil {
t.Error(err)
}
assert.Nil(t, tmpFile.Close())
assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte(`{"hello":"world"}`), 0644))

data, contentType, err := loadRawContent(tmpFile.Name())
assert.Nil(t, err)
assert.Equal(t, `{"hello":"world"}`, string(data))
assert.False(t, isBinary(contentType))
}

func TestTextContentType(t *testing.T) {
var tmpFile *os.File
var err error
if tmpFile, err = ioutil.TempFile("", "camel-k-*.json"); err != nil {
t.Error(err)
}
assert.Nil(t, tmpFile.Close())
assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte(`{"hello":"world"}`), 0644))

data, contentType, compressed, err := loadTextContent(tmpFile.Name(), false)
assert.Nil(t, err)
assert.Equal(t, `{"hello":"world"}`, string(data))
assert.False(t, isBinary(contentType))
assert.False(t, compressed)
}

func TestTextCompressed(t *testing.T) {
var tmpFile *os.File
var err error
if tmpFile, err = ioutil.TempFile("", "camel-k-*.json"); err != nil {
t.Error(err)
}
assert.Nil(t, tmpFile.Close())
assert.Nil(t, ioutil.WriteFile(tmpFile.Name(), []byte(`{"hello":"world"}`), 0644))

data, contentType, compressed, err := loadTextContent(tmpFile.Name(), true)
assert.Nil(t, err)
assert.NotEqual(t, `{"hello":"world"}`, string(data))
assert.False(t, isBinary(contentType))
assert.True(t, compressed)
}

func TestIsBinary(t *testing.T) {
assert.True(t, isBinary("image/jpeg"))
assert.True(t, isBinary("application/zip"))
assert.False(t, isBinary("text/plain"))
}
2 changes: 1 addition & 1 deletion pkg/cmd/util_dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func getTopLevelDependencies(catalog *camel.RuntimeCatalog, args []string) ([]st

// Invoke the dependency inspector code for each source file
for _, source := range args {
data, _, err := loadContent(source, false, false)
data, _, _, err := loadTextContent(source, false)
if err != nil {
return []string{}, err
}
Expand Down
Loading

0 comments on commit de1b258

Please sign in to comment.