Repository: apache/camel-k
Pull request #1526 (2020-06-13 21:20:17)

 #1321 add support for nodeport type for services
This PR add support for _**NodePort**_ type for _service_ trait. Some basic tests for _trait_ flag on _run_ also included. 


Patch:
diff --git a/docs/modules/ROOT/pages/developers.adoc b/docs/modules/ROOT/pages/developers.adoc
index c3664dc8b..815e0c2ab 100644
--- a/docs/modules/ROOT/pages/developers.adoc
+++ b/docs/modules/ROOT/pages/developers.adoc
@@ -45,7 +45,6 @@ This is a high-level overview of the project structure:
 | Path						| Content
 | https://github.com/apache/camel-k/tree/master/build[/build]			| Contains the Docker and Maven build configuration.
 | https://github.com/apache/camel-k/tree/master/cmd[/cmd]			| Contains the entry points (the *main* functions) for the **camel-k** binary (manager) and the **kamel** client tool.
-| https://github.com/apache/camel-k/tree/master/build[/build]		| Contains build data used by the operator-sdk.
 | https://github.com/apache/camel-k/tree/master/deploy[/deploy]		| Contains Kubernetes resource files that are used by the **kamel** client during installation. The `/deploy/resources.go` file is kept in sync with the content of the directory (`make build-embed-resources`), so that resources can be used from within the go code.
 | https://github.com/apache/camel-k/tree/master/docs[/docs]			| Contains the documentation website based on https://antora.org/[Antora].
 | https://github.com/apache/camel-k/tree/master/e2e[/e2e]			| Include integration tests to ensure that the software interacts correctly with Kubernetes and OpenShift.
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 882cd48d7..8f31e3d94 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -35,6 +35,8 @@ type serviceTrait struct {
 	BaseTrait `property:",squash"`
 	// To automatically detect from the code if a Service needs to be created.
 	Auto *bool `property:"auto"`
+	// Enable Service to be exposed as NodePort
+	NodePort *bool `property:"nodeport"`
 }
 
 const (
@@ -103,11 +105,21 @@ func (t *serviceTrait) Configure(e *Environment) (bool, error) {
 	return true, nil
 }
 
+func (t *serviceTrait) isNodeport() bool {
+	return t.NodePort == nil || *t.NodePort
+
+}
+
 func (t *serviceTrait) Apply(e *Environment) error {
 	svc := e.Resources.GetServiceForIntegration(e.Integration)
 	// add a new service if not already created
 	if svc == nil {
 		svc = getServiceFor(e)
+
+		if t.isNodeport() {
+			svc.Spec.Type = "NodePort"
+		}
+
 	}
 	e.Resources.Add(svc)
 	return nil


Test patch:
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index a0c9f9c66..466cf7e87 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -72,6 +72,35 @@ func TestRunWithAdditionalSourcesFlag(t *testing.T) {
 	assert.Len(t, runCmdOptions.Sources, 2)
 }
 
+func TestRunWithTraitFlag(t *testing.T) {
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	runCmdOptions := addTestRunCmd(options, rootCmd)
+
+	kamelTestPostAddCommandInit(t, rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "run", "--trait", "sample.enabled=true", "example.js")
+
+	assert.Nil(t, err)
+	assert.Equal(t, 1, len(runCmdOptions.Traits))
+	assert.Equal(t, "sample.enabled=true", runCmdOptions.Traits[0])
+}
+
+func TestRunWithAdditionalTraitFlag(t *testing.T) {
+	options, rootCmd := kamelTestPreAddCommandInit()
+
+	runCmdOptions := addTestRunCmd(options, rootCmd)
+
+	kamelTestPostAddCommandInit(t, rootCmd)
+
+	_, err := test.ExecuteCommand(rootCmd, "run", "--trait", "sample.enabled=true", "--trait", "sample.second=true", "example.js")
+
+	assert.Nil(t, err)
+	assert.Equal(t, 2, len(runCmdOptions.Traits))
+	assert.Equal(t, "sample.enabled=true", runCmdOptions.Traits[0])
+	assert.Equal(t, "sample.second=true", runCmdOptions.Traits[1])
+}
+
 //
 // This test does work when running as single test but fails
 // otherwise as we are using a global viper instance
diff --git a/pkg/trait/service_test.go b/pkg/trait/service_test.go
index 49a525e4d..229d4a4ea 100644
--- a/pkg/trait/service_test.go
+++ b/pkg/trait/service_test.go
@@ -295,3 +295,87 @@ func TestServiceWithCustomContainerName(t *testing.T) {
 		d.Spec.Template.Spec.Containers[0].Name,
 	)
 }
+
+func TestServiceWithNodeport(t *testing.T) {
+	catalog, err := camel.DefaultCatalog()
+	assert.Nil(t, err)
+
+	traitCatalog := NewCatalog(context.TODO(), nil)
+
+	environment := Environment{
+		CamelCatalog: catalog,
+		Catalog:      traitCatalog,
+		Integration: &v1.Integration{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      ServiceTestName,
+				Namespace: "ns",
+			},
+			Status: v1.IntegrationStatus{
+				Phase: v1.IntegrationPhaseDeploying,
+			},
+			Spec: v1.IntegrationSpec{
+				Profile: v1.TraitProfileKubernetes,
+				Sources: []v1.SourceSpec{
+					{
+						DataSpec: v1.DataSpec{
+							Name:        "routes.js",
+							Content:     `from("undertow:test").log("hello")`,
+							Compression: true,
+						},
+						Language: v1.LanguageJavaScript,
+					},
+				},
+				Traits: map[string]v1.TraitSpec{
+					"service": {
+						Configuration: map[string]string{
+							"enabled": 	"true",
+							"auto":    	"false",
+							"nodeport": "true",
+						},
+					},
+				},
+			},
+		},
+		IntegrationKit: &v1.IntegrationKit{
+			Status: v1.IntegrationKitStatus{
+				Phase: v1.IntegrationKitPhaseReady,
+			},
+		},
+		Platform: &v1.IntegrationPlatform{
+			Spec: v1.IntegrationPlatformSpec{
+				Cluster: v1.IntegrationPlatformClusterOpenShift,
+				Build: v1.IntegrationPlatformBuildSpec{
+					PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyS2I,
+					Registry:        v1.IntegrationPlatformRegistrySpec{Address: "registry"},
+				},
+			},
+		},
+		EnvVars:        make([]corev1.EnvVar, 0),
+		ExecutedTraits: make([]Trait, 0),
+		Resources:      kubernetes.NewCollection(),
+	}
+	environment.Platform.ResyncStatusFullConfig()
+
+	err = traitCatalog.apply(&environment)
+
+	assert.Nil(t, err)
+	assert.NotEmpty(t, environment.ExecutedTraits)
+	assert.NotNil(t, environment.GetTrait("deployment"))
+	assert.NotNil(t, environment.GetTrait("service"))
+	assert.NotNil(t, environment.GetTrait("container"))
+
+	s := environment.Resources.GetService(func(service *corev1.Service) bool {
+		return service.Name == ServiceTestName
+	})
+	d := environment.Resources.GetDeployment(func(deployment *appsv1.Deployment) bool {
+		return deployment.Name == ServiceTestName
+	})
+
+	assert.NotNil(t, d)
+	assert.NotNil(t, s)
+
+	assert.Len(t, s.Spec.Ports, 1)
+	assert.Equal(t, int32(80), s.Spec.Ports[0].Port)
+
+	assert.Equal(t, corev1.ServiceType("NodePort"), s.Spec.Type)
+}