Repository: apache/camel-k
Pull request #1479 (2020-06-01 11:06:17)

chore(builder): enable Spectrum to publish to authenticated registries
<!-- Description -->




<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```



Patch:
diff --git a/pkg/builder/spectrum/publisher.go b/pkg/builder/spectrum/publisher.go
index 5f89c694d..d1c8ec068 100644
--- a/pkg/builder/spectrum/publisher.go
+++ b/pkg/builder/spectrum/publisher.go
@@ -19,13 +19,16 @@ package spectrum
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path"
+	"path/filepath"
 
 	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/apache/camel-k/pkg/util/log"
 	spectrum "github.com/container-tools/spectrum/pkg/builder"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func publisher(ctx *builder.Context) error {
@@ -64,13 +67,23 @@ func publisher(ctx *builder.Context) error {
 		pullInsecure = false
 	}
 
+	registryConfigDir := ""
+	if pl.Spec.Build.Registry.Secret != "" {
+		registryConfigDir, err = mountSecret(ctx, pl.Spec.Build.Registry.Secret)
+		if err != nil {
+			return err
+		}
+		defer os.RemoveAll(registryConfigDir)
+	}
+
 	options := spectrum.Options{
-		Base:         ctx.BaseImage,
-		Target:       target,
-		PullInsecure: pullInsecure,
-		PushInsecure: pl.Status.Build.Registry.Insecure,
-		Stdout:       os.Stdout,
-		Stderr:       os.Stderr,
+		PullInsecure:  pullInsecure,
+		PushInsecure:  pl.Status.Build.Registry.Insecure,
+		PushConfigDir: registryConfigDir,
+		Base:          ctx.BaseImage,
+		Target:        target,
+		Stdout:        os.Stdout,
+		Stderr:        os.Stderr,
 	}
 
 	digest, err := spectrum.Build(options, libraryPath+":/deployments/dependencies")
@@ -82,3 +95,31 @@ func publisher(ctx *builder.Context) error {
 	ctx.Digest = digest
 	return nil
 }
+
+func mountSecret(ctx *builder.Context, name string) (string, error) {
+	dir, err := ioutil.TempDir("", "spectrum-secret-")
+	if err != nil {
+		return "", err
+	}
+
+	secret, err := ctx.CoreV1().Secrets(ctx.Namespace).Get(name, metav1.GetOptions{})
+	if err != nil {
+		os.RemoveAll(dir)
+		return "", err
+	}
+
+	for file, content := range secret.Data {
+		if err := ioutil.WriteFile(filepath.Join(dir, remap(file)), content, 0600); err != nil {
+			os.RemoveAll(dir)
+			return "", err
+		}
+	}
+	return dir, nil
+}
+
+func remap(name string) string {
+	if name == ".dockerconfigjson" {
+		return "config.json"
+	}
+	return name
+}
diff --git a/pkg/controller/integration/monitor.go b/pkg/controller/integration/monitor.go
index 344c694c8..4cea23e0f 100644
--- a/pkg/controller/integration/monitor.go
+++ b/pkg/controller/integration/monitor.go
@@ -27,6 +27,7 @@ import (
 	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/defaults"
 	"github.com/apache/camel-k/pkg/util/digest"
+	"github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 // NewMonitorAction creates a new monitoring action for an integration
@@ -91,6 +92,9 @@ func (action *monitorAction) Handle(ctx context.Context, integration *v1.Integra
 		}
 	}
 
+	// Mirror ready condition from the sub resource (e.g.ReplicaSet, Deployment, CronJob, ...) to the integration
+	kubernetes.MirrorReadyCondition(ctx, action.client, integration)
+
 	return integration, nil
 }
 
diff --git a/pkg/trait/cron.go b/pkg/trait/cron.go
index 853e68220..3f740b1d6 100644
--- a/pkg/trait/cron.go
+++ b/pkg/trait/cron.go
@@ -75,8 +75,7 @@ type cronTrait struct {
 	//
 	// It's required that all periodic consumers have the same period and it can be expressed as cron schedule (e.g. `1m` can be expressed as `0/1 * * * *`,
 	// while `35m` or `50s` cannot).
-	Auto     *bool `property:"auto"`
-	deployer deployerTrait
+	Auto *bool `property:"auto"`
 }
 
 var _ ControllerStrategySelector = &cronTrait{}
@@ -180,10 +179,6 @@ func (t *cronTrait) Configure(e *Environment) (bool, error) {
 			}
 		}
 	}
-	dt := e.Catalog.GetTrait("deployer")
-	if dt != nil {
-		t.deployer = *dt.(*deployerTrait)
-	}
 
 	// Fallback strategy can be implemented in any other controller
 	if t.Fallback != nil && *t.Fallback {
@@ -199,7 +194,7 @@ func (t *cronTrait) Configure(e *Environment) (bool, error) {
 	}
 
 	// CronJob strategy requires common schedule
-	strategy, err := e.DetermineControllerStrategy(t.Ctx, t.Client)
+	strategy, err := e.DetermineControllerStrategy()
 	if err != nil {
 		e.Integration.Status.SetErrorCondition(
 			v1.IntegrationConditionCronJobAvailable,
diff --git a/pkg/trait/deployer.go b/pkg/trait/deployer.go
index da55a7e1b..836811055 100644
--- a/pkg/trait/deployer.go
+++ b/pkg/trait/deployer.go
@@ -110,12 +110,6 @@ func (t *deployerTrait) Apply(e *Environment) error {
 			}
 			return nil
 		})
-
-		// Mirror ready condition from the sub resource to the integration
-		e.PostActions = append(e.PostActions, func(e *Environment) error {
-			kubernetes.MirrorReadyCondition(t.Ctx, t.Client, e.Integration)
-			return nil
-		})
 	}
 
 	return nil
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 088a72156..a56ec0688 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -33,7 +33,6 @@ import (
 // +camel-k:trait=deployment
 type deploymentTrait struct {
 	BaseTrait `property:",squash"`
-	deployer  deployerTrait
 }
 
 var _ ControllerStrategySelector = &deploymentTrait{}
@@ -64,7 +63,7 @@ func (t *deploymentTrait) Configure(e *Environment) (bool, error) {
 	//
 	// Don't deploy when a different strategy is needed (e.g. Knative, Cron)
 	//
-	strategy, err := e.DetermineControllerStrategy(t.Ctx, t.Client)
+	strategy, err := e.DetermineControllerStrategy()
 	if err != nil {
 		e.Integration.Status.SetErrorCondition(
 			v1.IntegrationConditionDeploymentAvailable,
@@ -85,15 +84,7 @@ func (t *deploymentTrait) Configure(e *Environment) (bool, error) {
 		return false, nil
 	}
 
-	enabled := e.IntegrationInPhase(v1.IntegrationPhaseDeploying)
-	if enabled {
-		dt := e.Catalog.GetTrait("deployer")
-		if dt != nil {
-			t.deployer = *dt.(*deployerTrait)
-		}
-	}
-
-	return enabled, nil
+	return e.IntegrationInPhase(v1.IntegrationPhaseDeploying), nil
 }
 
 func (t *deploymentTrait) SelectControllerStrategy(e *Environment) (*ControllerStrategy, error) {
@@ -146,12 +137,6 @@ func (t *deploymentTrait) IsPlatformTrait() bool {
 	return true
 }
 
-// **********************************
-//
-// Deployment
-//
-// **********************************
-
 func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	// create a copy to avoid sharing the underlying annotation map
 	annotations := make(map[string]string)
diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go
index 8bbe95cd5..bea151337 100644
--- a/pkg/trait/knative_service.go
+++ b/pkg/trait/knative_service.go
@@ -121,7 +121,7 @@ func (t *knativeServiceTrait) Configure(e *Environment) (bool, error) {
 		return false, nil
 	}
 
-	strategy, err := e.DetermineControllerStrategy(t.Ctx, t.Client)
+	strategy, err := e.DetermineControllerStrategy()
 	if err != nil {
 		e.Integration.Status.SetErrorCondition(
 			v1.IntegrationConditionKnativeServiceAvailable,
diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go
index e4b0e3493..9beeb2cb7 100644
--- a/pkg/trait/prometheus.go
+++ b/pkg/trait/prometheus.go
@@ -121,10 +121,17 @@ func (t *prometheusTrait) Apply(e *Environment) (err error) {
 	options := []string{strconv.Itoa(t.Port), path.Join(prometheusJmxExporterConfigMountPath, prometheusJmxExporterConfigFileName)}
 	container.Args = append(container.Args, "-javaagent:dependencies/io.prometheus.jmx.jmx_prometheus_javaagent-0.3.1.jar="+strings.Join(options, ":"))
 
-	// Add the container port
+	// Configure the Prometheus container port
 	containerPort := t.getContainerPort()
-	container.Ports = append(container.Ports, *containerPort)
-	condition.Message = fmt.Sprintf("%s(%s/%d)", container.Name, containerPort.Name, containerPort.ContainerPort)
+	controller, err := e.DetermineControllerStrategy()
+	if err != nil {
+		return err
+	}
+	// Skip declaring the Prometheus port when Knative is enabled, as only one container port is supported
+	if controller != ControllerStrategyKnativeService {
+		container.Ports = append(container.Ports, *containerPort)
+	}
+	condition.Message = fmt.Sprintf("%s(%d)", container.Name, containerPort.ContainerPort)
 
 	// Retrieve the service or create a new one if the service trait is enabled
 	serviceEnabled := false
@@ -135,14 +142,18 @@ func (t *prometheusTrait) Apply(e *Environment) (err error) {
 			serviceEnabled = serviceTrait.isEnabled()
 		}
 		if serviceEnabled {
-			// add a new service if not already created
+			// Add a new service if not already created
 			service = getServiceFor(e)
+			// Override the service name if none exists.
+			// This is required for Knative Serving, that checks no standard eponymous service exist
+			service.Name = service.Name + "-prometheus"
 			e.Resources.Add(service)
 		}
+	} else {
+		serviceEnabled = true
 	}
 
 	// Add the service port and service monitor resource
-	// A better strategy may be needed when the Knative profile is active
 	if serviceEnabled {
 		servicePort := t.getServicePort()
 		service.Spec.Ports = append(service.Spec.Ports, *servicePort)
@@ -168,7 +179,6 @@ func (t *prometheusTrait) Apply(e *Environment) (err error) {
 
 func (t *prometheusTrait) getContainerPort() *corev1.ContainerPort {
 	containerPort := corev1.ContainerPort{
-		Name:          prometheusPortName,
 		ContainerPort: int32(t.Port),
 		Protocol:      corev1.ProtocolTCP,
 	}
@@ -177,10 +187,11 @@ func (t *prometheusTrait) getContainerPort() *corev1.ContainerPort {
 
 func (t *prometheusTrait) getServicePort() *corev1.ServicePort {
 	servicePort := corev1.ServicePort{
-		Name:       prometheusPortName,
-		Port:       int32(t.Port),
-		Protocol:   corev1.ProtocolTCP,
-		TargetPort: intstr.FromString(prometheusPortName),
+		Name:     prometheusPortName,
+		Port:     int32(t.Port),
+		Protocol: corev1.ProtocolTCP,
+		// Avoid relying on named port, as Knative enforces specific values used for content negotiation
+		TargetPort: intstr.FromInt(t.Port),
 	}
 	return &servicePort
 }
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index 457afcf0f..6352c7a77 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -28,8 +28,6 @@ import (
 	"github.com/apache/camel-k/pkg/util"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	controller "sigs.k8s.io/controller-runtime/pkg/client"
-
 	"k8s.io/apimachinery/pkg/runtime"
 
 	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
@@ -223,7 +221,7 @@ const (
 	ControllerStrategyKnativeService ControllerStrategy = "knative-service"
 	ControllerStrategyCronJob        ControllerStrategy = "cron-job"
 
-	DefaultControllerStrategy ControllerStrategy = ControllerStrategyDeployment
+	DefaultControllerStrategy = ControllerStrategyDeployment
 )
 
 // GetTrait --
@@ -298,7 +296,7 @@ func (e *Environment) DetermineProfile() v1.TraitProfile {
 }
 
 // DetermineControllerStrategy determines the type of controller that should be used for the integration
-func (e *Environment) DetermineControllerStrategy(ctx context.Context, c controller.Reader) (ControllerStrategy, error) {
+func (e *Environment) DetermineControllerStrategy() (ControllerStrategy, error) {
 	defaultStrategy := DefaultControllerStrategy
 	for _, creator := range e.getControllerStrategyChoosers() {
 		if strategy, err := creator.SelectControllerStrategy(e); err != nil {


Test patch:
diff --git a/pkg/trait/deployer_test.go b/pkg/trait/deployer_test.go
index 7160f55d4..d5ffb9735 100644
--- a/pkg/trait/deployer_test.go
+++ b/pkg/trait/deployer_test.go
@@ -52,7 +52,7 @@ func TestApplyDeployerTraitDoesSucceed(t *testing.T) {
 	err := deployerTrait.Apply(environment)
 
 	assert.Nil(t, err)
-	assert.Len(t, environment.PostActions, 2)
+	assert.Len(t, environment.PostActions, 1)
 }
 
 func TestApplyDeployerTraitInInitializationPhaseDoesSucceed(t *testing.T) {
diff --git a/pkg/trait/deployment_test.go b/pkg/trait/deployment_test.go
index a20293f80..9798e723c 100644
--- a/pkg/trait/deployment_test.go
+++ b/pkg/trait/deployment_test.go
@@ -68,7 +68,6 @@ func TestConfigureDeploymentTraitDoesSucceed(t *testing.T) {
 
 	assert.Nil(t, err)
 	assert.True(t, configured)
-	assert.NotNil(t, deploymentTrait.deployer)
 }
 
 func TestConfigureDeploymentTraitWhileBuildingKitDoesNotSucceed(t *testing.T) {
diff --git a/pkg/trait/prometheus_test.go b/pkg/trait/prometheus_test.go
index 579b159fc..9d5e921d7 100644
--- a/pkg/trait/prometheus_test.go
+++ b/pkg/trait/prometheus_test.go
@@ -68,12 +68,11 @@ func TestApplyNominalPrometheusTraitDoesSucceed(t *testing.T) {
 
 	ports := container.Ports
 	assert.Len(t, ports, 1)
-	assert.Equal(t, "prometheus", ports[0].Name)
 	assert.Equal(t, int32(9779), ports[0].ContainerPort)
 	assert.Equal(t, corev1.ProtocolTCP, ports[0].Protocol)
 
 	service := environment.Resources.GetService(func(service *corev1.Service) bool {
-		return service.Name == "integration-name"
+		return service.Name == "integration-name-prometheus"
 	})
 	assert.NotNil(t, service)
 	assert.Len(t, service.Spec.Ports, 1)
@@ -103,7 +102,7 @@ func TestApplyPrometheusTraitWithoutContainerDoesNotSucceed(t *testing.T) {
 	assert.Equal(t, corev1.ConditionFalse, condition.Status)
 }
 
-func TestApplyPrometheusTraitWithServiceDoesNotSucceed(t *testing.T) {
+func TestApplyPrometheusTraitWithServiceDoesSucceed(t *testing.T) {
 	trait, environment := createNominalPrometheusTest()
 	environment.Resources = kubernetes.NewCollection(
 		&appsv1.Deployment{
@@ -146,8 +145,8 @@ func TestApplyPrometheusTraitWithServiceDoesNotSucceed(t *testing.T) {
 
 	assert.Len(t, environment.Integration.Status.Conditions, 1)
 	condition := environment.Integration.Status.Conditions[0]
-	assert.Equal(t, v1.IntegrationConditionServiceNotAvailableReason, condition.Reason)
-	assert.Equal(t, corev1.ConditionFalse, condition.Status)
+	assert.Equal(t, v1.IntegrationConditionPrometheusAvailableReason, condition.Reason)
+	assert.Equal(t, corev1.ConditionTrue, condition.Status)
 }
 
 func TestPrometheusTraitGetServiceMonitor(t *testing.T) {