You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2022/01/07 13:48:20 UTC

[camel-k] 14/24: feat(trait/openapi): support configmap configuration

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 46706b8f379291ddd8875a32dfd585203742757b
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Tue Dec 7 11:32:02 2021 +0100

    feat(trait/openapi): support configmap configuration
    
    Closes #2772
---
 examples/openapi/README.md        | 23 +++++++++++-
 examples/openapi/greetings.groovy |  2 +-
 examples/openapi/petstore.groovy  |  2 +-
 pkg/trait/openapi.go              | 73 ++++++++++++++++++++++++++++++---------
 pkg/util/digest/digest.go         |  8 +----
 5 files changed, 81 insertions(+), 27 deletions(-)

diff --git a/examples/openapi/README.md b/examples/openapi/README.md
index 41193d7..c8ca3c0 100644
--- a/examples/openapi/README.md
+++ b/examples/openapi/README.md
@@ -1,3 +1,24 @@
 # Open API Camel K examples
 
-Find useful examples about how to expose an Open API specification in a Camel K integration.
\ No newline at end of file
+Find useful examples about how to expose an Open API specification in a Camel K integration.
+
+## Greetings example
+
+Deploy the examples running
+
+```
+kamel run --dev --name greetings --open-api greetings-api.json greetings.groovy
+```
+
+Then you can test by calling the hello endpoint, ie:
+
+```
+$ curl -i http://192.168.49.2:31373/camel/greetings/hello
+HTTP/1.1 200 OK
+Accept: */*
+name: hello
+User-Agent: curl/7.68.0
+transfer-encoding: chunked
+
+Hello from hello
+```
\ No newline at end of file
diff --git a/examples/openapi/greetings.groovy b/examples/openapi/greetings.groovy
index a96fa64..90a09ce 100644
--- a/examples/openapi/greetings.groovy
+++ b/examples/openapi/greetings.groovy
@@ -17,7 +17,7 @@
  */
 
 //
-//  kamel run --dev --name greetings --open-api examples/greetings-api.json examples/greetings.groovy
+//  kamel run --dev --name greetings --open-api greetings-api.json greetings.groovy
 // 
 
 from('direct:greeting-api')
diff --git a/examples/openapi/petstore.groovy b/examples/openapi/petstore.groovy
index d382b21..880e22b 100644
--- a/examples/openapi/petstore.groovy
+++ b/examples/openapi/petstore.groovy
@@ -17,7 +17,7 @@
  */
 
 //
-//  kamel run --dev --name petstore --open-api examples/petstore-api.yaml examples/petstore.groovy
+//  kamel run --dev --name petstore --open-api petstore-api.yaml petstore.groovy
 // 
 
 from('direct:listPets')
diff --git a/pkg/trait/openapi.go b/pkg/trait/openapi.go
index 09d7ab5..b1ff98d 100644
--- a/pkg/trait/openapi.go
+++ b/pkg/trait/openapi.go
@@ -27,8 +27,6 @@ import (
 	"strconv"
 	"strings"
 
-	"go.uber.org/multierr"
-
 	"github.com/pkg/errors"
 
 	corev1 "k8s.io/api/core/v1"
@@ -52,6 +50,8 @@ import (
 // +camel-k:trait=openapi.
 type openAPITrait struct {
 	BaseTrait `property:",squash"`
+	// The configmaps holding the spec of the OpenAPI
+	Configmaps []string `property:"configmaps" json:"configmaps,omitempty"`
 }
 
 func newOpenAPITrait() Trait {
@@ -85,6 +85,10 @@ func (t *openAPITrait) Configure(e *Environment) (bool, error) {
 		}
 	}
 
+	if t.Configmaps != nil {
+		return e.IntegrationInPhase(v1.IntegrationPhaseInitialization), nil
+	}
+
 	return false, nil
 }
 
@@ -97,26 +101,63 @@ func (t *openAPITrait) Apply(e *Environment) error {
 		return err
 	}
 
-	for i, resource := range e.Integration.Spec.Resources {
+	generatedFromResources, err := t.generateFromResources(e, tmpDir)
+	if err != nil {
+		return os.RemoveAll(tmpDir)
+	}
+	generatedFromConfigmaps, err := t.generateFromConfigmaps(e, tmpDir)
+	if err != nil {
+		return os.RemoveAll(tmpDir)
+	}
+	if generatedFromConfigmaps != nil && len(generatedFromConfigmaps) > 0 {
+		generatedFromResources = append(generatedFromResources, generatedFromConfigmaps...)
+	}
+	e.Integration.Status.GeneratedSources = generatedFromResources
+
+	return os.RemoveAll(tmpDir)
+}
+
+func (t *openAPITrait) generateFromResources(e *Environment, tmpDir string) ([]v1.SourceSpec, error) {
+	dataSpecs := make([]v1.DataSpec, 0, len(e.Integration.Spec.Resources))
+	for _, resource := range e.Integration.Spec.Resources {
 		if resource.Type != v1.ResourceTypeOpenAPI {
 			continue
 		}
 		if resource.Name == "" {
-			return multierr.Append(
-				fmt.Errorf("no name defined for the openapi resource: %v", resource),
-				os.RemoveAll(tmpDir))
+			return nil, fmt.Errorf("no name defined for the openapi resource: %v", resource)
+		}
+		dataSpecs = append(dataSpecs, resource.DataSpec)
+	}
+
+	return t.generateFromDataSpecs(e, tmpDir, dataSpecs)
+}
+
+func (t *openAPITrait) generateFromConfigmaps(e *Environment, tmpDir string) ([]v1.SourceSpec, error) {
+	dataSpecs := make([]v1.DataSpec, 0, len(t.Configmaps))
+	for _, configmap := range t.Configmaps {
+		cm := kubernetes.LookupConfigmap(e.Ctx, e.Client, e.Integration.Namespace, configmap)
+		// Iterate over each configmap key which may hold a different OpenAPI spec
+		for k, v := range cm.Data {
+			dataSpecs = append(dataSpecs, v1.DataSpec{
+				Name:        k,
+				Content:     v,
+				Compression: false,
+			})
 		}
+	}
 
-		generatedContentName := fmt.Sprintf("%s-openapi-%03d", e.Integration.Name, i)
+	return t.generateFromDataSpecs(e, tmpDir, dataSpecs)
+}
 
+func (t *openAPITrait) generateFromDataSpecs(e *Environment, tmpDir string, specs []v1.DataSpec) ([]v1.SourceSpec, error) {
+	generatedSources := make([]v1.SourceSpec, 0, len(e.Integration.Status.GeneratedSources))
+	for i, resource := range specs {
+		generatedContentName := fmt.Sprintf("%s-openapi-%03d", e.Integration.Name, i)
+		generatedSourceName := strings.TrimSuffix(resource.Name, filepath.Ext(resource.Name)) + ".xml"
 		// Generate configmap or reuse existing one
 		if err := t.generateOpenAPIConfigMap(e, resource, tmpDir, generatedContentName); err != nil {
-			return errors.Wrapf(err, "cannot generate configmap for openapi resource %s", resource.Name)
+			return nil, errors.Wrapf(err, "cannot generate configmap for openapi resource %s", resource.Name)
 		}
-
-		generatedSourceName := strings.TrimSuffix(resource.Name, filepath.Ext(resource.Name)) + ".xml"
-		generatedSources := make([]v1.SourceSpec, 0, len(e.Integration.Status.GeneratedSources))
-
 		if e.Integration.Status.GeneratedSources != nil {
 			// Filter out the previously generated source
 			for _, x := range e.Integration.Status.GeneratedSources {
@@ -135,14 +176,12 @@ func (t *openAPITrait) Apply(e *Environment) error {
 			},
 			Language: v1.LanguageXML,
 		})
-
-		e.Integration.Status.GeneratedSources = generatedSources
 	}
 
-	return os.RemoveAll(tmpDir)
+	return generatedSources, nil
 }
 
-func (t *openAPITrait) generateOpenAPIConfigMap(e *Environment, resource v1.ResourceSpec, tmpDir, generatedContentName string) error {
+func (t *openAPITrait) generateOpenAPIConfigMap(e *Environment, resource v1.DataSpec, tmpDir, generatedContentName string) error {
 	cm := corev1.ConfigMap{}
 	key := client.ObjectKey{
 		Namespace: e.Integration.Namespace,
@@ -176,7 +215,7 @@ func (t *openAPITrait) generateOpenAPIConfigMap(e *Environment, resource v1.Reso
 	return t.createNewOpenAPIConfigMap(e, resource, tmpDir, generatedContentName)
 }
 
-func (t *openAPITrait) createNewOpenAPIConfigMap(e *Environment, resource v1.ResourceSpec, tmpDir, generatedContentName string) error {
+func (t *openAPITrait) createNewOpenAPIConfigMap(e *Environment, resource v1.DataSpec, tmpDir, generatedContentName string) error {
 	tmpDir = path.Join(tmpDir, generatedContentName)
 	err := os.MkdirAll(tmpDir, os.ModePerm)
 	if err != nil {
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index 4d999a1..566a873 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -160,7 +160,7 @@ func ComputeForIntegrationKit(kit *v1.IntegrationKit) (string, error) {
 }
 
 // ComputeForResource returns a digest for the specific resource.
-func ComputeForResource(res v1.ResourceSpec) (string, error) {
+func ComputeForResource(res v1.DataSpec) (string, error) {
 	hash := sha256.New()
 	// Operator version is relevant
 	if _, err := hash.Write([]byte(defaults.Version)); err != nil {
@@ -173,18 +173,12 @@ func ComputeForResource(res v1.ResourceSpec) (string, error) {
 	if _, err := hash.Write([]byte(res.Name)); err != nil {
 		return "", err
 	}
-	if _, err := hash.Write([]byte(res.Type)); err != nil {
-		return "", err
-	}
 	if _, err := hash.Write([]byte(res.ContentKey)); err != nil {
 		return "", err
 	}
 	if _, err := hash.Write([]byte(res.ContentRef)); err != nil {
 		return "", err
 	}
-	if _, err := hash.Write([]byte(res.MountPath)); err != nil {
-		return "", err
-	}
 	if _, err := hash.Write([]byte(strconv.FormatBool(res.Compression))); err != nil {
 		return "", err
 	}