This is an automated email from the ASF dual-hosted git repository. pcongiusti pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit d7282d5c16c30c48b94cd96de302e946ece8fb0d Author: Pasquale Congiusti <[email protected]> AuthorDate: Thu Dec 2 10:13:43 2021 +0100 feat(cmd/run): convert --resource/--config to related trait --- pkg/cmd/run.go | 48 ++++++++++++++++++++-------------- pkg/cmd/run_help.go | 56 ++++++++++++++-------------------------- pkg/trait/container.go | 4 ++- pkg/trait/trait_types.go | 29 +++++++-------------- pkg/util/resource/config.go | 43 ++++++++++++------------------ pkg/util/resource/config_test.go | 15 ----------- 6 files changed, 77 insertions(+), 118 deletions(-) diff --git a/pkg/cmd/run.go b/pkg/cmd/run.go index 5ff377e..fcebe0b 100644 --- a/pkg/cmd/run.go +++ b/pkg/cmd/run.go @@ -570,29 +570,35 @@ func (o *runCmdOptions) createOrUpdateIntegration(cmd *cobra.Command, c client.C } generatedConfigmaps := make([]*corev1.ConfigMap, 0) - for _, res := range o.Resources { - if config, parseErr := resource.ParseResource(res); parseErr == 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 + config, err := resource.ParseResource(res) + if err != nil { + return nil, err } + // We try to autogenerate a configmap + maybeGenCm, err := parseConfigAndGenCm(o.Context, c, config, integration, o.Compression) + if err != nil { + return nil, err + } + if maybeGenCm != nil { + generatedConfigmaps = append(generatedConfigmaps, maybeGenCm) + } + o.Traits = append(o.Traits, convertToTrait(config.String(), "container.resources")) } - - for _, item := range o.Configs { - if config, parseErr := resource.ParseConfig(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 + for _, conf := range o.Configs { + config, err := resource.ParseResource(conf) + if err != nil { + return nil, err + } + // We try to autogenerate a configmap + maybeGenCm, err := parseConfigAndGenCm(o.Context, c, config, integration, o.Compression) + if err != nil { + return nil, err + } + if maybeGenCm != nil { + generatedConfigmaps = append(generatedConfigmaps, maybeGenCm) } + o.Traits = append(o.Traits, convertToTrait(config.String(), "container.configs")) } for _, resource := range o.OpenAPIs { @@ -714,6 +720,10 @@ func addResource(ctx context.Context, resourceLocation string, integrationSpec * return nil } +func convertToTrait(value, traitParameter string) string { + return fmt.Sprintf("%s=%s", traitParameter, value) +} + func convertToTraitParameter(value, traitParameter string) ([]string, error) { traits := make([]string, 0) props, err := extractProperties(value) diff --git a/pkg/cmd/run_help.go b/pkg/cmd/run_help.go index dcbacf5..e9f9758 100644 --- a/pkg/cmd/run_help.go +++ b/pkg/cmd/run_help.go @@ -44,65 +44,47 @@ func hashFrom(contents ...[]byte) string { return fmt.Sprintf("%x", hash.Sum(nil)) } -// ApplyConfigOption will set the proper --config option behavior to the IntegrationSpec. -func ApplyConfigOption(ctx context.Context, config *resource.Config, integration *v1.Integration, c client.Client, - namespace string, enableCompression bool) (*corev1.ConfigMap, error) { - // A config option cannot specify destination path - if config.DestinationPath() != "" { - return nil, fmt.Errorf("cannot specify a destination path for this option type") - } - return applyOption(ctx, config, integration, c, namespace, enableCompression, v1.ResourceTypeConfig) -} - -// ApplyResourceOption will set the proper --resource option behavior to the IntegrationSpec. -func ApplyResourceOption(ctx context.Context, config *resource.Config, integration *v1.Integration, c client.Client, - namespace string, enableCompression bool) (*corev1.ConfigMap, error) { - return applyOption(ctx, config, integration, c, namespace, enableCompression, v1.ResourceTypeData) -} - -func applyOption(ctx context.Context, config *resource.Config, integration *v1.Integration, - c client.Client, namespace string, enableCompression bool, resourceType v1.ResourceType) (*corev1.ConfigMap, error) { - var maybeGenCm *corev1.ConfigMap +func parseConfigAndGenCm(ctx context.Context, c client.Client, config *resource.Config, integration *v1.Integration, enableCompression bool) (*corev1.ConfigMap, error) { switch config.StorageType() { case resource.StorageTypeConfigmap: - cm := kubernetes.LookupConfigmap(ctx, c, namespace, config.Name()) + cm := kubernetes.LookupConfigmap(ctx, c, integration.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 maybeGenCm, fmt.Errorf("you cannot provide a binary config, use a text file instead") + config.Name(), integration.Namespace) + } else if config.ContentType() != resource.ContentTypeData && cm.BinaryData != nil { + return nil, fmt.Errorf("you cannot provide a binary config, use a text file instead") } case resource.StorageTypeSecret: - secret := kubernetes.LookupSecret(ctx, c, namespace, config.Name()) + secret := kubernetes.LookupSecret(ctx, c, integration.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) + config.Name(), integration.Namespace) } case resource.StorageTypeFile: // Don't allow a binary non compressed resource rawData, contentType, err := loadRawContent(ctx, config.Name()) if err != nil { - return maybeGenCm, err + return nil, err } - if resourceType != v1.ResourceTypeData && !enableCompression && isBinary(contentType) { - return maybeGenCm, fmt.Errorf("you cannot provide a binary config, use a text file or check --resource flag instead") + if config.ContentType() != resource.ContentTypeData && !enableCompression && isBinary(contentType) { + return nil, 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 maybeGenCm, err + resourceType := v1.ResourceTypeData + if config.ContentType() == resource.ContentTypeText { + resourceType = v1.ResourceTypeConfig } - maybeGenCm, err = resource.ConvertFileToConfigmap(ctx, c, resourceSpec, config, integration.Namespace, integration.Name, resourceType) + resourceSpec, err := binaryOrTextResource(path.Base(config.Name()), rawData, contentType, enableCompression, resourceType, config.DestinationPath()) if err != nil { - return maybeGenCm, err + return nil, err } + + return resource.ConvertFileToConfigmap(ctx, c, config, integration.Namespace, integration.Name, resourceSpec.Content, resourceSpec.RawContent) default: // Should never reach this - return maybeGenCm, fmt.Errorf("invalid option type %s", config.StorageType()) + return nil, fmt.Errorf("invalid option type %s", config.StorageType()) } - integration.Spec.AddConfigurationAsResource(string(config.StorageType()), config.Name(), string(resourceType), config.DestinationPath(), config.Key()) - - return maybeGenCm, nil + return nil, nil } func binaryOrTextResource(fileName string, data []byte, contentType string, base64Compression bool, resourceType v1.ResourceType, destinationPath string) (v1.ResourceSpec, error) { diff --git a/pkg/trait/container.go b/pkg/trait/container.go index cc0ca1b..ddbea8e 100644 --- a/pkg/trait/container.go +++ b/pkg/trait/container.go @@ -360,7 +360,9 @@ func (t *containerTrait) configureVolumesAndMounts(vols *[]corev1.Volume, mnts * func (t *containerTrait) mountResource(vols *[]corev1.Volume, mnts *[]corev1.VolumeMount, conf *utilResource.Config) { refName := kubernetes.SanitizeLabel(conf.Name()) vol := getVolume(refName, string(conf.StorageType()), conf.Name(), conf.Key(), conf.Key()) - mnt := getMount(refName, conf.DestinationPath(), "") + mntPath := getMountPoint(conf.Name(), conf.DestinationPath(), string(conf.StorageType()), string(conf.ContentType())) + // No need to specify a subpath, as we mount the entire configmap/secret + mnt := getMount(refName, mntPath, "") *vols = append(*vols, *vol) *mnts = append(*mnts, *mnt) diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go index b83f986..9de2616 100644 --- a/pkg/trait/trait_types.go +++ b/pkg/trait/trait_types.go @@ -510,7 +510,7 @@ func (e *Environment) configureVolumesAndMounts(vols *[]corev1.Volume, mnts *[]c // for _, configmaps := range e.collectConfigurations("configmap") { refName := kubernetes.SanitizeLabel(configmaps["value"]) - mountPath := getConfigmapMountPoint(configmaps["value"], configmaps["resourceMountPoint"], configmaps["resourceType"]) + mountPath := getMountPoint(configmaps["value"], configmaps["resourceMountPoint"], "configmap", configmaps["resourceType"]) vol := getVolume(refName, "configmap", configmaps["value"], configmaps["resourceKey"], configmaps["resourceKey"]) mnt := getMount(refName, mountPath, "") @@ -523,7 +523,7 @@ func (e *Environment) configureVolumesAndMounts(vols *[]corev1.Volume, mnts *[]c // for _, secret := range e.collectConfigurations("secret") { refName := kubernetes.SanitizeLabel(secret["value"]) - mountPath := getSecretMountPoint(secret["value"], secret["resourceMountPoint"], secret["resourceType"]) + mountPath := getMountPoint(secret["value"], secret["resourceMountPoint"], "secret", secret["resourceType"]) vol := getVolume(refName, "secret", secret["value"], secret["resourceKey"], secret["resourceKey"]) mnt := getMount(refName, mountPath, "") @@ -633,30 +633,19 @@ func getResourcePath(resourceName string, maybePath string, resourceType v1.Reso return path.Join(camel.ConfigResourcesMountPath, resourceName) } -func getConfigmapMountPoint(resourceName string, maybeMountPoint string, resourceType string) string { - // If the mount point is specified, we'll return it - if maybeMountPoint != "" { - return maybeMountPoint +func getMountPoint(resourceName string, mountPoint string, storagetype, resourceType string) string { + if mountPoint != "" { + return mountPoint } if resourceType == "data" { return path.Join(camel.ResourcesDefaultMountPath, resourceName) } - - // Default, config type - return path.Join(camel.ConfigConfigmapsMountPath, resourceName) -} - -func getSecretMountPoint(resourceName string, maybeMountPoint string, resourceType string) string { - // If the mount point is specified, we'll return it - if maybeMountPoint != "" { - return maybeMountPoint - } - if resourceType == "data" { - return path.Join(camel.ResourcesDefaultMountPath, resourceName) + defaultMountPoint := camel.ConfigConfigmapsMountPath + if storagetype == "secret" { + defaultMountPoint = camel.ConfigSecretsMountPath } - // Default, config type - return path.Join(camel.ConfigSecretsMountPath, resourceName) + return path.Join(defaultMountPoint, resourceName) } func (e *Environment) collectConfigurationValues(configurationType string) []string { diff --git a/pkg/util/resource/config.go b/pkg/util/resource/config.go index d900622..6a53b01 100644 --- a/pkg/util/resource/config.go +++ b/pkg/util/resource/config.go @@ -25,7 +25,6 @@ import ( "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" @@ -33,8 +32,6 @@ import ( k8serrors "k8s.io/apimachinery/pkg/api/errors" ) -var invalidPaths = []string{"/etc/camel", "/deployments/dependencies"} - // Config represents a config option. type Config struct { storageType StorageType @@ -69,19 +66,17 @@ func (config *Config) Key() string { return config.resourceKey } -// Validate checks if the DestinationPath is correctly configured. -func (config *Config) Validate() error { - if config.destinationPath == "" { - return nil +// String represents the unparsed value of the resource. +func (config *Config) String() string { + s := fmt.Sprintf("%s:%s", config.storageType, config.resourceName) + if config.resourceKey != "" { + s = fmt.Sprintf("%s/%s", s, config.resourceKey) } - - // Check for invalid path - for _, invalidPath := range invalidPaths { - if config.destinationPath == invalidPath || strings.HasPrefix(config.destinationPath, invalidPath+"/") { - return fmt.Errorf("you cannot mount a file under %s path", invalidPath) - } + if config.destinationPath != "" { + s = fmt.Sprintf("%s@%s", s, config.destinationPath) } - return nil + + return s } // StorageType represent a resource/config type such as configmap, secret or local file. @@ -153,7 +148,7 @@ func parseCMOrSecretValue(value string) (resource string, maybeKey string, maybe return groups[1], groups[3], groups[5] } -// ParseResource will parse and return a Config. +// ParseResource will parse a resource and return a Config. func ParseResource(item string) (*Config, error) { // Deprecated: ensure backward compatibility with `--resource filename` format until version 1.5.x // then replace with parse() func directly @@ -169,7 +164,7 @@ func ParseResource(item string) (*Config, error) { return resource, nil } -// ParseConfig will parse and return a Config. +// ParseConfig will parse a config and return a Config. func ParseConfig(item string) (*Config, error) { return parse(item, ContentTypeText) } @@ -197,24 +192,20 @@ func parse(item string, contentType ContentType) (*Config, error) { return nil, fmt.Errorf("could not match config, secret or file configuration as %s", item) } - configurationOption := newConfig(cot, contentType, value) - if err := configurationOption.Validate(); err != nil { - return nil, err - } - return configurationOption, nil + return newConfig(cot, contentType, value), nil } // ConvertFileToConfigmap convert a local file resource type in a configmap type // taking care to create the Configmap on the cluster. The method will change the value of config parameter // to reflect the conversion applied transparently. -func ConvertFileToConfigmap(ctx context.Context, c client.Client, resourceSpec v1.ResourceSpec, config *Config, - namespace string, integrationName string, resourceType v1.ResourceType) (*corev1.ConfigMap, error) { +func ConvertFileToConfigmap(ctx context.Context, c client.Client, config *Config, namespace string, integrationName string, + content string, rawContent []byte) (*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 { + if config.ContentType() == ContentTypeData { config.destinationPath = camel.ResourcesDefaultMountPath } else { config.destinationPath = camel.ConfigResourcesMountPath @@ -223,8 +214,8 @@ func ConvertFileToConfigmap(ctx context.Context, c client.Client, resourceSpec v config.resourceKey = filepath.Base(config.DestinationPath()) config.destinationPath = filepath.Dir(config.DestinationPath()) } - genCmName := fmt.Sprintf("cm-%s", hashFrom([]byte(integrationName), []byte(resourceSpec.Content), resourceSpec.RawContent)) - cm := kubernetes.NewConfigmap(namespace, genCmName, filepath.Base(config.Name()), config.Key(), resourceSpec.Content, resourceSpec.RawContent) + genCmName := fmt.Sprintf("cm-%s", hashFrom([]byte(integrationName), []byte(content), rawContent)) + cm := kubernetes.NewConfigmap(namespace, genCmName, filepath.Base(config.Name()), config.Key(), content, rawContent) err := c.Create(ctx, cm) if err != nil { if k8serrors.IsAlreadyExists(err) { diff --git a/pkg/util/resource/config_test.go b/pkg/util/resource/config_test.go index 5d7fb24..bb28d85 100644 --- a/pkg/util/resource/config_test.go +++ b/pkg/util/resource/config_test.go @@ -141,18 +141,3 @@ func TestParseConfigOptionAllParams(t *testing.T) { assert.Equal(t, "", parsedFile3.Key()) assert.Equal(t, "", parsedFile3.DestinationPath()) } - -func TestValidateFileLocation(t *testing.T) { - validLocation := "file:my-file.txt@/tmp/another-name.xml" - etcCamelLocation := "configmap:my-cm@/etc/camel/configmaps" - deploymentsDepsLocation := "secret:my-sec@/deployments/dependencies" - - _, err := ParseConfig(validLocation) - assert.Nil(t, err) - _, err = ParseConfig(etcCamelLocation) - assert.NotNil(t, err) - assert.Equal(t, "you cannot mount a file under /etc/camel path", err.Error()) - _, err = ParseConfig(deploymentsDepsLocation) - assert.NotNil(t, err) - assert.Equal(t, "you cannot mount a file under /deployments/dependencies path", err.Error()) -}
