This is an automated email from the ASF dual-hosted git repository.

acosentino pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/main by this push:
     new 062b21a09 Kamelet - Inject secret in Vaults - Azure Key Vault (#4798)
062b21a09 is described below

commit 062b21a09a96d734e886d1e9a0e49f4769dbce72
Author: Andrea Cosentino <anco...@gmail.com>
AuthorDate: Thu Oct 5 16:20:42 2023 +0200

    Kamelet - Inject secret in Vaults - Azure Key Vault (#4798)
    
    * Kamelet - Inject secret in Vaults - Azure Key Vault
    
    Signed-off-by: Andrea Cosentino <anco...@gmail.com>
    
    * Kamelet - Inject secret in Vaults - Azure Key Vault
    
    Signed-off-by: Andrea Cosentino <anco...@gmail.com>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <anco...@gmail.com>
---
 addons/vault/azure/azure_key_vault.go              |  36 ++++++-
 addons/vault/azure/azure_key_vault_test.go         | 110 ++++++++++++++++++++-
 docs/modules/traits/pages/aws-secrets-manager.adoc |   4 +-
 docs/modules/traits/pages/azure-key-vault.adoc     |   8 +-
 resources/traits.yaml                              |  25 +++--
 5 files changed, 165 insertions(+), 18 deletions(-)

diff --git a/addons/vault/azure/azure_key_vault.go 
b/addons/vault/azure/azure_key_vault.go
index 76ecee985..5ab1ab0f6 100644
--- a/addons/vault/azure/azure_key_vault.go
+++ b/addons/vault/azure/azure_key_vault.go
@@ -18,8 +18,11 @@ limitations under the License.
 package azure
 
 import (
+       "regexp"
        "strconv"
 
+       "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
        traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
        "github.com/apache/camel-k/v2/pkg/trait"
@@ -50,7 +53,9 @@ type Trait struct {
        TenantID string `property:"tenant-id" json:"tenantId,omitempty"`
        // The Azure Client Id for accessing Key Vault
        ClientID string `property:"client-id" json:"clientId,omitempty"`
-       // The Azure Client Secret for accessing Key Vault
+       // The Azure Client Secret for accessing Key Vault. This could be a 
plain text or a configmap/secret.
+       // The content of the azure key vault client secret is expected to be a 
text containing a valid Client Secret.
+       // Syntax: [configmap|secret]:name[/key], where name represents the 
resource name, key optionally represents the resource key to be filtered 
(default key value = azure-key-vault-client-secret).
        ClientSecret string `property:"client-secret" 
json:"clientSecret,omitempty"`
        // The Azure Vault Name for accessing Key Vault
        VaultName string `property:"vault-name" json:"vaultName,omitempty"`
@@ -66,7 +71,9 @@ type Trait struct {
        EventhubConnectionString string `property:"eventhub-connection-string" 
json:"eventhubConnectionString,omitempty"`
        // If Refresh is enabled, the account name for Azure Storage Blob 
service used to save checkpoint while consuming from Eventhub
        BlobAccountName string `property:"blob-account-name" 
json:"blobAccountName,omitempty"`
-       // If Refresh is enabled, the access key for Azure Storage Blob service 
used to save checkpoint while consuming from Eventhub
+       // If Refresh is enabled, the access key for Azure Storage Blob service 
used to save checkpoint while consuming from Eventhub. This could be a plain 
text or a configmap/secret.
+       // The content of the azure key vault blob access key is expected to be 
a text containing a valid Access Key for Azure Storage Blob.
+       // Syntax: [configmap|secret]:name[/key], where name represents the 
resource name, key optionally represents the resource key to be filtered 
(default key value = azure-storage-blob-access-key).
        BlobAccessKey string `property:"blob-access-key" 
json:"blobAccessKey,omitempty"`
        // If Refresh is enabled, the container name for Azure Storage Blob 
service used to save checkpoint while consuming from Eventhub
        BlobContainerName string `property:"blob-container-name" 
json:"blobContainerName,omitempty"`
@@ -104,6 +111,7 @@ func (t *azureKeyVaultTrait) Configure(environment 
*trait.Environment) (bool, er
 }
 
 func (t *azureKeyVaultTrait) Apply(environment *trait.Environment) error {
+       rex := 
regexp.MustCompile(`^(configmap|secret):([a-zA-Z0-9][a-zA-Z0-9-]*)(/([a-zA-Z0-9].*))?$`)
        if environment.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
                
util.StringSliceUniqueAdd(&environment.Integration.Status.Capabilities, 
v1.CapabilityAzureKeyVault)
                // Deprecated
@@ -112,9 +120,30 @@ func (t *azureKeyVaultTrait) Apply(environment 
*trait.Environment) error {
        }
 
        if environment.IntegrationInRunningPhases() {
+               hits := rex.FindAllStringSubmatch(t.ClientSecret, -1)
+               if len(hits) >= 1 {
+                       var res, _ = v1.DecodeValueSource(t.ClientSecret, 
"azure-key-vault-client-secret", "The Azure Key Vault Client Secret provided is 
not valid")
+                       if secretValue, err := 
kubernetes.ResolveValueSource(environment.Ctx, environment.Client, 
environment.Platform.Namespace, &res); err != nil {
+                               return err
+                       } else if secretValue != "" {
+                               
environment.ApplicationProperties["camel.vault.azure.clientSecret"] = 
string([]byte(secretValue))
+                       }
+               } else {
+                       
environment.ApplicationProperties["camel.vault.azure.clientSecret"] = 
t.ClientSecret
+               }
+               hits = rex.FindAllStringSubmatch(t.BlobAccessKey, -1)
+               if len(hits) >= 1 {
+                       var res, _ = v1.DecodeValueSource(t.BlobAccessKey, 
"azure-storage-blob-access-key", "The Azure Storage Blob Access Key provided is 
not valid")
+                       if secretValue, err := 
kubernetes.ResolveValueSource(environment.Ctx, environment.Client, 
environment.Platform.Namespace, &res); err != nil {
+                               return err
+                       } else if secretValue != "" {
+                               
environment.ApplicationProperties["camel.vault.azure.blobAccessKey"] = 
string([]byte(secretValue))
+                       }
+               } else {
+                       
environment.ApplicationProperties["camel.vault.azure.blobAccessKey"] = 
t.BlobAccessKey
+               }
                environment.ApplicationProperties["camel.vault.azure.tenantId"] 
= t.TenantID
                environment.ApplicationProperties["camel.vault.azure.clientId"] 
= t.ClientID
-               
environment.ApplicationProperties["camel.vault.azure.clientSecret"] = 
t.ClientSecret
                
environment.ApplicationProperties["camel.vault.azure.vaultName"] = t.VaultName
                
environment.ApplicationProperties["camel.vault.azure.refreshEnabled"] = 
strconv.FormatBool(*t.RefreshEnabled)
                
environment.ApplicationProperties["camel.main.context-reload-enabled"] = 
strconv.FormatBool(*t.ContextReloadEnabled)
@@ -125,7 +154,6 @@ func (t *azureKeyVaultTrait) Apply(environment 
*trait.Environment) error {
                
environment.ApplicationProperties["camel.vault.azure.eventhubConnectionString"] 
= t.EventhubConnectionString
                
environment.ApplicationProperties["camel.vault.azure.blobAccountName"] = 
t.BlobAccountName
                
environment.ApplicationProperties["camel.vault.azure.blobContainerName"] = 
t.BlobContainerName
-               
environment.ApplicationProperties["camel.vault.azure.blobAccessKey"] = 
t.BlobAccessKey
        }
 
        return nil
diff --git a/addons/vault/azure/azure_key_vault_test.go 
b/addons/vault/azure/azure_key_vault_test.go
index 6de912212..388643605 100644
--- a/addons/vault/azure/azure_key_vault_test.go
+++ b/addons/vault/azure/azure_key_vault_test.go
@@ -20,6 +20,9 @@ package azure
 import (
        "testing"
 
+       "github.com/apache/camel-k/v2/pkg/util/test"
+       corev1 "k8s.io/api/core/v1"
+
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
        "k8s.io/utils/pointer"
 
@@ -28,6 +31,7 @@ import (
        "github.com/apache/camel-k/v2/pkg/util/camel"
 
        "github.com/stretchr/testify/assert"
+       "k8s.io/apimachinery/pkg/runtime"
 )
 
 func TestAzureKeyVaultTraitApply(t *testing.T) {
@@ -52,25 +56,127 @@ func TestAzureKeyVaultTraitApply(t *testing.T) {
        assert.Equal(t, "my-vault", 
e.ApplicationProperties["camel.vault.azure.vaultName"])
 }
 
-func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, 
error)) *trait.Environment {
+func TestAzureKeyVaultTraitApplyWithConfigmapAndRefresh(t *testing.T) {
+       e := createEnvironment(t, camel.QuarkusCatalog, &corev1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "my-configmap1",
+               },
+               Data: map[string]string{
+                       "azure-client-secret": "my-secret-key",
+               },
+       }, &corev1.ConfigMap{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "my-configmap2",
+               },
+               Data: map[string]string{
+                       "azure-storage-blob-key": "my-access-key",
+               },
+       })
+       azure := NewAzureKeyVaultTrait()
+       secrets, _ := azure.(*azureKeyVaultTrait)
+       secrets.Enabled = pointer.Bool(true)
+       secrets.TenantID = "tenant-id"
+       secrets.ClientID = "client-id"
+       secrets.ClientSecret = "configmap:my-configmap1/azure-client-secret"
+       secrets.VaultName = "my-vault"
+       secrets.RefreshEnabled = pointer.Bool(true)
+       secrets.BlobAccessKey = "configmap:my-configmap2/azure-storage-blob-key"
+       secrets.BlobAccountName = "camel-k"
+       secrets.BlobContainerName = "camel-k-container"
+       ok, err := secrets.Configure(e)
+       assert.Nil(t, err)
+       assert.True(t, ok)
+
+       err = secrets.Apply(e)
+       assert.Nil(t, err)
+
+       assert.Equal(t, "client-id", 
e.ApplicationProperties["camel.vault.azure.clientId"])
+       assert.Equal(t, "my-secret-key", 
e.ApplicationProperties["camel.vault.azure.clientSecret"])
+       assert.Equal(t, "tenant-id", 
e.ApplicationProperties["camel.vault.azure.tenantId"])
+       assert.Equal(t, "my-vault", 
e.ApplicationProperties["camel.vault.azure.vaultName"])
+       assert.Equal(t, "camel-k", 
e.ApplicationProperties["camel.vault.azure.blobAccountName"])
+       assert.Equal(t, "camel-k-container", 
e.ApplicationProperties["camel.vault.azure.blobContainerName"])
+       assert.Equal(t, "my-access-key", 
e.ApplicationProperties["camel.vault.azure.blobAccessKey"])
+       assert.True(t, true, 
e.ApplicationProperties["camel.vault.azure.refreshEnabled"])
+}
+
+func TestAzureKeyVaultTraitApplyWithSecretAndRefresh(t *testing.T) {
+       e := createEnvironment(t, camel.QuarkusCatalog, &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "my-secret1",
+               },
+               Data: map[string][]byte{
+                       "azure-client-secret": []byte("my-secret-key"),
+               },
+       }, &corev1.Secret{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "my-secret2",
+               },
+               Data: map[string][]byte{
+                       "azure-storage-blob-key": []byte("my-access-key"),
+               },
+       })
+       azure := NewAzureKeyVaultTrait()
+       secrets, _ := azure.(*azureKeyVaultTrait)
+       secrets.Enabled = pointer.Bool(true)
+       secrets.TenantID = "tenant-id"
+       secrets.ClientID = "client-id"
+       secrets.ClientSecret = "secret:my-secret1/azure-client-secret"
+       secrets.VaultName = "my-vault"
+       secrets.RefreshEnabled = pointer.Bool(true)
+       secrets.BlobAccessKey = "secret:my-secret2/azure-storage-blob-key"
+       secrets.BlobAccountName = "camel-k"
+       secrets.BlobContainerName = "camel-k-container"
+       ok, err := secrets.Configure(e)
+       assert.Nil(t, err)
+       assert.True(t, ok)
+
+       err = secrets.Apply(e)
+       assert.Nil(t, err)
+
+       assert.Equal(t, "client-id", 
e.ApplicationProperties["camel.vault.azure.clientId"])
+       assert.Equal(t, "my-secret-key", 
e.ApplicationProperties["camel.vault.azure.clientSecret"])
+       assert.Equal(t, "tenant-id", 
e.ApplicationProperties["camel.vault.azure.tenantId"])
+       assert.Equal(t, "my-vault", 
e.ApplicationProperties["camel.vault.azure.vaultName"])
+       assert.Equal(t, "camel-k", 
e.ApplicationProperties["camel.vault.azure.blobAccountName"])
+       assert.Equal(t, "camel-k-container", 
e.ApplicationProperties["camel.vault.azure.blobContainerName"])
+       assert.Equal(t, "my-access-key", 
e.ApplicationProperties["camel.vault.azure.blobAccessKey"])
+       assert.True(t, true, 
e.ApplicationProperties["camel.vault.azure.refreshEnabled"])
+}
+
+func createEnvironment(t *testing.T, catalogGen func() (*camel.RuntimeCatalog, 
error), objects ...runtime.Object) *trait.Environment {
        t.Helper()
 
        catalog, err := catalogGen()
+       client, _ := test.NewFakeClient(objects...)
        assert.Nil(t, err)
 
        e := trait.Environment{
                CamelCatalog:          catalog,
                ApplicationProperties: make(map[string]string),
+               Client:                client,
        }
 
        it := v1.Integration{
                ObjectMeta: metav1.ObjectMeta{
-                       Name: "test",
+                       Namespace: "test",
+                       Name:      "test",
                },
                Status: v1.IntegrationStatus{
                        Phase: v1.IntegrationPhaseDeploying,
                },
        }
+       platform := v1.IntegrationPlatform{
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: "test",
+                       Name:      "test",
+               },
+       }
        e.Integration = &it
+       e.Platform = &platform
        return &e
 }
diff --git a/docs/modules/traits/pages/aws-secrets-manager.adoc 
b/docs/modules/traits/pages/aws-secrets-manager.adoc
index 4a67be3c1..d221f8280 100644
--- a/docs/modules/traits/pages/aws-secrets-manager.adoc
+++ b/docs/modules/traits/pages/aws-secrets-manager.adoc
@@ -50,8 +50,8 @@ Syntax: [configmap\|secret]:name[/key], where name represents 
the resource name,
 | aws-secrets-manager.secret-key
 | string
 | The AWS Secret Key to use. This could be a plain text or a configmap/secret
-       // The content of the aws secret key is expected to be a text 
containing a valid AWS secret key.
-       // Syntax: [configmap\|secret]:name[/key], where name represents the 
resource name, key optionally represents the resource key to be filtered 
(default key value = aws-secret-key).
+The content of the aws secret key is expected to be a text containing a valid 
AWS secret key.
+Syntax: [configmap\|secret]:name[/key], where name represents the resource 
name, key optionally represents the resource key to be filtered (default key 
value = aws-secret-key).
 
 | aws-secrets-manager.region
 | string
diff --git a/docs/modules/traits/pages/azure-key-vault.adoc 
b/docs/modules/traits/pages/azure-key-vault.adoc
index 48303a037..a5d8ffc69 100644
--- a/docs/modules/traits/pages/azure-key-vault.adoc
+++ b/docs/modules/traits/pages/azure-key-vault.adoc
@@ -51,7 +51,9 @@ The following configuration options are available:
 
 | azure-key-vault.client-secret
 | string
-| The Azure Client Secret for accessing Key Vault
+| The Azure Client Secret for accessing Key Vault. This could be a plain text 
or a configmap/secret.
+The content of the azure key vault client secret is expected to be a text 
containing a valid Client Secret.
+Syntax: [configmap\|secret]:name[/key], where name represents the resource 
name, key optionally represents the resource key to be filtered (default key 
value = azure-key-vault-client-secret).
 
 | azure-key-vault.vault-name
 | string
@@ -83,7 +85,9 @@ The following configuration options are available:
 
 | azure-key-vault.blob-access-key
 | string
-| If Refresh is enabled, the access key for Azure Storage Blob service used to 
save checkpoint while consuming from Eventhub
+| If Refresh is enabled, the access key for Azure Storage Blob service used to 
save checkpoint while consuming from Eventhub. This could be a plain text or a 
configmap/secret.
+The content of the azure key vault blob access key is expected to be a text 
containing a valid Access Key for Azure Storage Blob.
+Syntax: [configmap\|secret]:name[/key], where name represents the resource 
name, key optionally represents the resource key to be filtered (default key 
value = azure-storage-blob-access-key).
 
 | azure-key-vault.blob-container-name
 | string
diff --git a/resources/traits.yaml b/resources/traits.yaml
index 826996269..5fdd4f75e 100755
--- a/resources/traits.yaml
+++ b/resources/traits.yaml
@@ -116,11 +116,11 @@ traits:
       (default key value = aws-access-key).'
   - name: secret-key
     type: string
-    description: "The AWS Secret Key to use. This could be a plain text or a 
configmap/secret
-      \t// The content of the aws secret key is expected to be a text 
containing a
-      valid AWS secret key. \t// Syntax: [configmap|secret]:name[/key], where 
name
-      represents the resource name, key optionally represents the resource key 
to
-      be filtered (default key value = aws-secret-key)."
+    description: 'The AWS Secret Key to use. This could be a plain text or a 
configmap/secret
+      The content of the aws secret key is expected to be a text containing a 
valid
+      AWS secret key. Syntax: [configmap|secret]:name[/key], where name 
represents
+      the resource name, key optionally represents the resource key to be 
filtered
+      (default key value = aws-secret-key).'
   - name: region
     type: string
     description: The AWS Region to use
@@ -179,7 +179,11 @@ traits:
     description: The Azure Client Id for accessing Key Vault
   - name: client-secret
     type: string
-    description: The Azure Client Secret for accessing Key Vault
+    description: 'The Azure Client Secret for accessing Key Vault. This could 
be a
+      plain text or a configmap/secret. The content of the azure key vault 
client
+      secret is expected to be a text containing a valid Client Secret. 
Syntax: [configmap|secret]:name[/key],
+      where name represents the resource name, key optionally represents the 
resource
+      key to be filtered (default key value = azure-key-vault-client-secret).'
   - name: vault-name
     type: string
     description: The Azure Vault Name for accessing Key Vault
@@ -207,8 +211,13 @@ traits:
       used to save checkpoint while consuming from Eventhub
   - name: blob-access-key
     type: string
-    description: If Refresh is enabled, the access key for Azure Storage Blob 
service
-      used to save checkpoint while consuming from Eventhub
+    description: 'If Refresh is enabled, the access key for Azure Storage Blob 
service
+      used to save checkpoint while consuming from Eventhub. This could be a 
plain
+      text or a configmap/secret. The content of the azure key vault blob 
access key
+      is expected to be a text containing a valid Access Key for Azure Storage 
Blob.
+      Syntax: [configmap|secret]:name[/key], where name represents the 
resource name,
+      key optionally represents the resource key to be filtered (default key 
value
+      = azure-storage-blob-access-key).'
   - name: blob-container-name
     type: string
     description: If Refresh is enabled, the container name for Azure Storage 
Blob

Reply via email to