Skip to content

Commit

Permalink
feat(cmd/run): autogenerated configmap for resource
Browse files Browse the repository at this point in the history
  • Loading branch information
squakez committed Nov 24, 2021
1 parent 04d778e commit 3b2ba13
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

//
// To run this integrations use:
// kamel run --resource resources-data.txt --compression=true resource-file-base64-encoded-route.groovy --dev
// kamel run --resource file:resources-data.txt --compression=true resource-file-base64-encoded-route.groovy --dev
//

from('timer:resources-bas64')
Expand Down
2 changes: 1 addition & 1 deletion examples/user-config/resource-file-binary-route.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

//
// To run this integrations use:
// kamel run --resource resources-data.zip resource-file-binary-route.groovy -d camel-zipfile --dev
// kamel run --resource file:resources-data.zip resource-file-binary-route.groovy -d camel-zipfile --dev
//

from('file:/etc/camel/resources/?fileName=resources-data.zip&noop=true&idempotent=false')
Expand Down
38 changes: 24 additions & 14 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,10 +569,26 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
return nil, err
}

generatedConfigmaps := make([]*corev1.ConfigMap, 0)

for _, resource := range o.Resources {
if config, parseErr := ParseResourceOption(resource); parseErr == nil {
if applyResourceOptionErr := ApplyResourceOption(config, &integration.Spec, c, namespace, o.Compression); applyResourceOptionErr != nil {
if genCm, applyResourceOptionErr := ApplyResourceOption(o.Context, config, integration, c, namespace, o.Compression); applyResourceOptionErr != nil {
return nil, applyResourceOptionErr
} else if genCm != nil {
generatedConfigmaps = append(generatedConfigmaps, genCm)
}
} else {
return nil, parseErr
}
}

for _, item := range o.Configs {
if config, parseErr := ParseConfigOption(item); parseErr == nil {
if genCm, applyConfigOptionErr := ApplyConfigOption(o.Context, config, integration, c, namespace, o.Compression); applyConfigOptionErr != nil {
return nil, applyConfigOptionErr
} else if genCm != nil {
generatedConfigmaps = append(generatedConfigmaps, genCm)
}
} else {
return nil, parseErr
Expand Down Expand Up @@ -620,15 +636,6 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C
o.Traits = append(o.Traits, buildPropsTraits...)
}

for _, item := range o.Configs {
if config, parseErr := ParseConfigOption(item); parseErr == nil {
if applyConfigOptionErr := ApplyConfigOption(config, &integration.Spec, c, namespace, o.Compression); applyConfigOptionErr != nil {
return nil, applyConfigOptionErr
}
} else {
return nil, parseErr
}
}
for _, item := range o.ConfigMaps {
integration.Spec.AddConfiguration("configmap", item)
}
Expand Down Expand Up @@ -671,18 +678,21 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C

if existing == nil {
err = c.Create(o.Context, integration)
fmt.Printf("Integration \"%s\" created\n", name)
} else {
err = c.Patch(o.Context, integration, ctrl.MergeFromWithOptions(existing, ctrl.MergeFromWithOptimisticLock{}))
fmt.Printf("Integration \"%s\" updated\n", name)
}

if err != nil {
return nil, err
}

if existing == nil {
fmt.Printf("Integration \"%s\" created\n", name)
} else {
fmt.Printf("Integration \"%s\" updated\n", name)
if generatedConfigmaps != nil {
err = bindGeneratedConfigmapsToIntegration(o.Context, c, integration, generatedConfigmaps)
if err != nil {
return integration, err
}
}
return integration, nil
}
Expand Down
124 changes: 98 additions & 26 deletions pkg/cmd/run_help.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@ package cmd

import (
"context"
"crypto/sha1"
"fmt"
"path"
"path/filepath"
"regexp"
"strings"

v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/client"
"github.com/apache/camel-k/pkg/util/camel"
"github.com/apache/camel-k/pkg/util/kubernetes"
"github.com/magiconair/properties"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var invalidPaths = []string{"/etc/camel", "/deployments/dependencies"}
Expand Down Expand Up @@ -181,67 +187,107 @@ func parseOption(item string) (*RunConfigOption, error) {
return configurationOption, nil
}

func applyOption(config *RunConfigOption, integrationSpec *v1.IntegrationSpec,
c client.Client, namespace string, enableCompression bool, resourceType v1.ResourceType) error {
func applyOption(ctx context.Context, config *RunConfigOption, integration *v1.Integration,
c client.Client, namespace string, enableCompression bool, resourceType v1.ResourceType) (*corev1.ConfigMap, error) {
var maybeGenCm *corev1.ConfigMap
switch config.configType {
case ConfigOptionTypeConfigmap:
cm := kubernetes.LookupConfigmap(context.Background(), c, namespace, config.Name())
cm := kubernetes.LookupConfigmap(ctx, c, namespace, config.Name())
if cm == nil {
fmt.Printf("Warn: %s Configmap not found in %s namespace, make sure to provide it before the Integration can run\n",
config.Name(), namespace)
} else if resourceType != v1.ResourceTypeData && cm.BinaryData != nil {
return fmt.Errorf("you cannot provide a binary config, use a text file instead")
return maybeGenCm, fmt.Errorf("you cannot provide a binary config, use a text file instead")
}
integrationSpec.AddConfigurationAsResource(config.Type(), config.Name(), string(resourceType), config.DestinationPath(), config.Key())
case ConfigOptionTypeSecret:
secret := kubernetes.LookupSecret(context.Background(), c, namespace, config.Name())
secret := kubernetes.LookupSecret(ctx, c, namespace, config.Name())
if secret == nil {
fmt.Printf("Warn: %s Secret not found in %s namespace, make sure to provide it before the Integration can run\n",
config.Name(), namespace)
}
integrationSpec.AddConfigurationAsResource(string(config.configType), config.Name(), string(resourceType), config.DestinationPath(), config.Key())
case ConfigOptionTypeFile:
// Don't allow a file size longer than 1 MiB
fileSize, err := fileSize(config.Name())
printSize := fmt.Sprintf("%.2f", float64(fileSize)/Megabyte)
if err != nil {
return err
} else if fileSize > Megabyte {
return fmt.Errorf("you cannot provide a file larger than 1 MB (it was %s MB), check configmap option or --volume instead", printSize)
}
// Don't allow a binary non compressed resource
rawData, contentType, err := loadRawContent(config.Name())
if err != nil {
return err
return maybeGenCm, err
}
if resourceType != v1.ResourceTypeData && !enableCompression && isBinary(contentType) {
return fmt.Errorf("you cannot provide a binary config, use a text file or check --resource flag instead")
return maybeGenCm, fmt.Errorf("you cannot provide a binary config, use a text file or check --resource flag instead")
}
resourceSpec, err := binaryOrTextResource(path.Base(config.Name()), rawData, contentType, enableCompression, resourceType, config.DestinationPath())
if err != nil {
return err
return maybeGenCm, err
}
maybeGenCm, err = convertFileToConfigmap(ctx, c, resourceSpec, config, integration.Namespace, resourceType)
if err != nil {
return maybeGenCm, err
}
integrationSpec.AddResources(resourceSpec)
default:
// Should never reach this
return fmt.Errorf("invalid option type %s", config.configType)
return maybeGenCm, fmt.Errorf("invalid option type %s", config.configType)
}

return nil
integration.Spec.AddConfigurationAsResource(config.Type(), config.Name(), string(resourceType), config.DestinationPath(), config.Key())

return maybeGenCm, nil
}

func convertFileToConfigmap(ctx context.Context, c client.Client, resourceSpec v1.ResourceSpec, config *RunConfigOption,
namespace string, resourceType v1.ResourceType) (*corev1.ConfigMap, error) {
if config.DestinationPath() == "" {
config.resourceKey = filepath.Base(config.Name())
// As we are changing the resource to a configmap type
// we need to declare the mount path not to use the
// default behavior of a configmap (which include a subdirectory with the configmap name)
if resourceType == v1.ResourceTypeData {
config.destinationPath = camel.ResourcesDefaultMountPath
} else {
config.destinationPath = camel.ConfigResourcesMountPath
}
} else {
config.resourceKey = filepath.Base(config.DestinationPath())
config.destinationPath = filepath.Dir(config.DestinationPath())
}
genCmName := fmt.Sprintf("cm-%s", hashFrom([]byte(resourceSpec.Content), resourceSpec.RawContent))
cm := kubernetes.NewConfigmap(namespace, genCmName, config.Name(), config.Key(), resourceSpec.Content, resourceSpec.RawContent)
err := c.Create(ctx, cm)
if err != nil {
if k8serrors.IsAlreadyExists(err) {
// We'll reuse it, as is
} else {
return cm, err
}
}
config.configType = ConfigOptionTypeConfigmap
config.resourceName = cm.Name

return cm, nil
}

func hashFrom(contents ...[]byte) string {
// SHA1 because we need to limit the lenght to less than 64 chars
hash := sha1.New()
for _, c := range contents {
hash.Write(c)
}

return fmt.Sprintf("%x", hash.Sum(nil))
}

// ApplyConfigOption will set the proper --config option behavior to the IntegrationSpec
func ApplyConfigOption(config *RunConfigOption, integrationSpec *v1.IntegrationSpec, c client.Client, namespace string, enableCompression bool) error {
func ApplyConfigOption(ctx context.Context, config *RunConfigOption, integration *v1.Integration, c client.Client,
namespace string, enableCompression bool) (*corev1.ConfigMap, error) {
// A config option cannot specify destination path
if config.DestinationPath() != "" {
return fmt.Errorf("cannot specify a destination path for this option type")
return nil, fmt.Errorf("cannot specify a destination path for this option type")
}
return applyOption(config, integrationSpec, c, namespace, enableCompression, v1.ResourceTypeConfig)
return applyOption(ctx, config, integration, c, namespace, enableCompression, v1.ResourceTypeConfig)
}

// ApplyResourceOption will set the proper --resource option behavior to the IntegrationSpec
func ApplyResourceOption(config *RunConfigOption, integrationSpec *v1.IntegrationSpec, c client.Client, namespace string, enableCompression bool) error {
return applyOption(config, integrationSpec, c, namespace, enableCompression, v1.ResourceTypeData)
func ApplyResourceOption(ctx context.Context, config *RunConfigOption, integration *v1.Integration, c client.Client,
namespace string, enableCompression bool) (*corev1.ConfigMap, error) {
return applyOption(ctx, config, integration, c, namespace, enableCompression, v1.ResourceTypeData)
}

func binaryOrTextResource(fileName string, data []byte, contentType string, base64Compression bool, resourceType v1.ResourceType, destinationPath string) (v1.ResourceSpec, error) {
Expand Down Expand Up @@ -319,3 +365,29 @@ func extractProperties(value string) (*properties.Properties, error) {
func keyValueProps(value string) (*properties.Properties, error) {
return properties.Load([]byte(value), properties.UTF8)
}

func bindGeneratedConfigmapsToIntegration(ctx context.Context, c client.Client, i *v1.Integration, configmaps []*corev1.ConfigMap) error {
controller := true
blockOwnerDeletion := true
for _, cm := range configmaps {
cm.ObjectMeta.Labels[v1.IntegrationLabel] = i.Name
cm.ObjectMeta.Labels["camel.apache.org/autogenerated"] = "true"
// set owner references
cm.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
{
Kind: v1.IntegrationKind,
APIVersion: v1.SchemeGroupVersion.String(),
Name: i.Name,
UID: i.UID,
Controller: &controller,
BlockOwnerDeletion: &blockOwnerDeletion,
},
}
err := c.Update(ctx, cm)
if err != nil {
return err
}
}

return nil
}
5 changes: 3 additions & 2 deletions pkg/trait/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/util"
"github.com/apache/camel-k/pkg/util/camel"
"github.com/apache/camel-k/pkg/util/defaults"
"github.com/apache/camel-k/pkg/util/envvar"
"github.com/apache/camel-k/pkg/util/kubernetes"
Expand Down Expand Up @@ -245,8 +246,8 @@ func (t *containerTrait) configureContainer(e *Environment) error {
}

envvar.SetVal(&container.Env, "CAMEL_K_DIGEST", e.Integration.Status.Digest)
envvar.SetVal(&container.Env, "CAMEL_K_CONF", path.Join(basePath, "application.properties"))
envvar.SetVal(&container.Env, "CAMEL_K_CONF_D", confDPath)
envvar.SetVal(&container.Env, "CAMEL_K_CONF", path.Join(camel.BasePath, "application.properties"))
envvar.SetVal(&container.Env, "CAMEL_K_CONF_D", camel.ConfDPath)

e.addSourcesProperties()

Expand Down
5 changes: 3 additions & 2 deletions pkg/trait/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
package trait

import (
"github.com/apache/camel-k/pkg/util/camel"
"github.com/apache/camel-k/pkg/util/defaults"
"github.com/apache/camel-k/pkg/util/envvar"
"github.com/apache/camel-k/pkg/util/property"
Expand Down Expand Up @@ -73,8 +74,8 @@ func (t *environmentTrait) Apply(e *Environment) error {
envvar.SetVal(&e.EnvVars, envVarCamelKIntegration, e.Integration.Name)
}
envvar.SetVal(&e.EnvVars, envVarCamelKRuntimeVersion, e.RuntimeVersion)
envvar.SetVal(&e.EnvVars, envVarMountPathConfigMaps, configConfigmapsMountPath)
envvar.SetVal(&e.EnvVars, envVarMountPathSecrets, configSecretsMountPath)
envvar.SetVal(&e.EnvVars, envVarMountPathConfigMaps, camel.ConfigConfigmapsMountPath)
envvar.SetVal(&e.EnvVars, envVarMountPathSecrets, camel.ConfigSecretsMountPath)

if IsNilOrTrue(t.ContainerMeta) {
envvar.SetValFrom(&e.EnvVars, envVarNamespace, "metadata.namespace")
Expand Down
5 changes: 3 additions & 2 deletions pkg/trait/jvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
"github.com/apache/camel-k/pkg/builder"
"github.com/apache/camel-k/pkg/util"
"github.com/apache/camel-k/pkg/util/camel"
)

// The JVM trait is used to configure the JVM that runs the integration.
Expand Down Expand Up @@ -108,8 +109,8 @@ func (t *jvmTrait) Apply(e *Environment) error {
classpath := strset.New()

classpath.Add("./resources")
classpath.Add(configResourcesMountPath)
classpath.Add(resourcesDefaultMountPath)
classpath.Add(camel.ConfigResourcesMountPath)
classpath.Add(camel.ResourcesDefaultMountPath)
if t.Classpath != "" {
classpath.Add(strings.Split(t.Classpath, ":")...)
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/trait/jvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,12 @@ func TestApplyJvmTraitWithDeploymentResource(t *testing.T) {

assert.Nil(t, err)

cp := strset.New("./resources", configResourcesMountPath, resourcesDefaultMountPath, "/mount/path").List()
cp := strset.New("./resources", camel.ConfigResourcesMountPath, camel.ResourcesDefaultMountPath, "/mount/path").List()
sort.Strings(cp)

assert.Equal(t, []string{
"-cp",
fmt.Sprintf("./resources:%s:%s:/mount/path", configResourcesMountPath, resourcesDefaultMountPath),
fmt.Sprintf("./resources:%s:%s:/mount/path", camel.ConfigResourcesMountPath, camel.ResourcesDefaultMountPath),
"io.quarkus.bootstrap.runner.QuarkusEntryPoint",
}, d.Spec.Template.Spec.Containers[0].Args)
}
Expand All @@ -134,12 +134,12 @@ func TestApplyJvmTraitWithKNativeResource(t *testing.T) {

assert.Nil(t, err)

cp := strset.New("./resources", configResourcesMountPath, resourcesDefaultMountPath, "/mount/path").List()
cp := strset.New("./resources", camel.ConfigResourcesMountPath, camel.ResourcesDefaultMountPath, "/mount/path").List()
sort.Strings(cp)

assert.Equal(t, []string{
"-cp",
fmt.Sprintf("./resources:%s:%s:/mount/path", configResourcesMountPath, resourcesDefaultMountPath),
fmt.Sprintf("./resources:%s:%s:/mount/path", camel.ConfigResourcesMountPath, camel.ResourcesDefaultMountPath),
"io.quarkus.bootstrap.runner.QuarkusEntryPoint",
}, s.Spec.Template.Spec.Containers[0].Args)
}
Expand Down Expand Up @@ -243,7 +243,7 @@ func TestApplyJvmTraitWithClasspath(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, []string{
"-cp",
fmt.Sprintf("./resources:%s:%s:/mount/path:%s:%s", configResourcesMountPath, resourcesDefaultMountPath,
fmt.Sprintf("./resources:%s:%s:/mount/path:%s:%s", camel.ConfigResourcesMountPath, camel.ResourcesDefaultMountPath,
"/path/to/another/dep.jar", "/path/to/my-dep.jar"),
"io.quarkus.bootstrap.runner.QuarkusEntryPoint",
}, d.Spec.Template.Spec.Containers[0].Args)
Expand Down
3 changes: 2 additions & 1 deletion pkg/trait/service_binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.
package trait

import (
"github.com/apache/camel-k/pkg/util/camel"
"github.com/apache/camel-k/pkg/util/reference"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -77,7 +78,7 @@ func (t *serviceBindingTrait) Apply(e *Environment) error {
if secret != nil {
e.Resources.Add(secret)
e.ApplicationProperties["quarkus.kubernetes-service-binding.enabled"] = "true"
e.ApplicationProperties["SERVICE_BINDING_ROOT"] = serviceBindingsMountPath
e.ApplicationProperties["SERVICE_BINDING_ROOT"] = camel.ServiceBindingsMountPath
e.ServiceBindingSecret = secret.GetName()
}
return nil
Expand Down
Loading

0 comments on commit 3b2ba13

Please sign in to comment.