Repository: apache/camel-k
Pull request #769 (2019-06-23 19:54:55)

chore(integration controller): replace custom reconcile trigger with watch

nicolaferraro: Conflicts @lburgazzoli 

Patch:
diff --git a/Gopkg.lock b/Gopkg.lock
index f5f1eacd8..4e74fdbd7 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -1173,6 +1173,7 @@
     "k8s.io/api/core/v1",
     "k8s.io/api/extensions/v1beta1",
     "k8s.io/api/rbac/v1",
+    "k8s.io/api/rbac/v1beta1",
     "k8s.io/apimachinery/pkg/api/errors",
     "k8s.io/apimachinery/pkg/api/meta",
     "k8s.io/apimachinery/pkg/api/resource",
diff --git a/pkg/apis/camel/v1alpha1/common_types.go b/pkg/apis/camel/v1alpha1/common_types.go
index 4a927dd87..00a45d181 100644
--- a/pkg/apis/camel/v1alpha1/common_types.go
+++ b/pkg/apis/camel/v1alpha1/common_types.go
@@ -75,7 +75,8 @@ type Configurable interface {
 
 // MavenSpec --
 type MavenSpec struct {
-	Settings ValueSource `json:"settings,omitempty"`
+	Settings ValueSource     `json:"settings,omitempty"`
+	Timeout  metav1.Duration `json:"timeout,omitempty"`
 }
 
 // ValueSource --
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index 60e0f4865..a0f5962c7 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -902,6 +902,7 @@ func (in *IntegrationStatus) DeepCopy() *IntegrationStatus {
 func (in *MavenSpec) DeepCopyInto(out *MavenSpec) {
 	*out = *in
 	in.Settings.DeepCopyInto(&out.Settings)
+	out.Timeout = in.Timeout
 	return
 }
 
diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index 32d089099..1d3499ade 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -264,9 +264,9 @@ func sanitizeDependencies(ctx *Context) error {
 
 func computeDependencies(ctx *Context) error {
 	mc := maven.NewContext(path.Join(ctx.Path, "maven"), ctx.Maven.Project)
-	mc.Settings = ctx.Maven.Settings
-	mc.SettingsData = ctx.Maven.SettingsData
-	mc.AddArguments(maven.ExtraOptions(ctx.Build.Platform.Build.LocalRepository)...)
+	mc.SettingsContent = ctx.Maven.SettingsData
+	mc.LocalRepository = ctx.Build.Platform.Build.LocalRepository
+	mc.Timeout = ctx.Build.Platform.Build.Maven.Timeout.Duration
 	mc.AddArgumentf("org.apache.camel.k:camel-k-maven-plugin:%s:generate-dependency-list", ctx.Build.RuntimeVersion)
 
 	if err := maven.Run(mc); err != nil {
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 1ad1a1945..7574b0733 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -115,7 +115,6 @@ type Context struct {
 
 	Maven struct {
 		Project      maven.Project
-		Settings     *maven.Settings
 		SettingsData []byte
 	}
 }
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index f7e21a3ce..885fb0a00 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -212,58 +212,62 @@ func (o *installCmdOptions) install(_ *cobra.Command, _ []string) error {
 			platform.Spec.Build.Timeout.Duration = d
 		}
 
-		if len(o.mavenRepositories) > 0 {
-			o.mavenSettings = fmt.Sprintf("configmap:%s-maven-settings/settings.xml", platform.Name)
+		o.mavenSettings = fmt.Sprintf("configmap:%s-maven-settings/settings.xml", platform.Name)
 
-			settings := maven.NewSettings()
-			repositories := make([]maven.Repository, 0, len(o.mavenRepositories))
+		settings := maven.NewSettings()
+		repositories := make([]maven.Repository, 0, len(o.mavenRepositories))
 
-			for i, r := range o.mavenRepositories {
-				repository := maven.NewRepository(r)
-				if repository.ID == "" {
-					repository.ID = fmt.Sprintf("repository-%03d", i)
-				}
-
-				repositories = append(repositories, repository)
+		for i, r := range o.mavenRepositories {
+			repository := maven.NewRepository(r)
+			if repository.ID == "" {
+				repository.ID = fmt.Sprintf("repository-%03d", i)
 			}
 
-			settings.Profiles = []maven.Profile{
-				{
-					ID: "maven-settings",
-					Activation: maven.Activation{
-						ActiveByDefault: true,
-					},
-					Repositories:       repositories,
-					PluginRepositories: repositories,
-				},
-			}
+			repositories = append(repositories, repository)
+		}
 
-			data, err := util.EncodeXML(settings)
-			if err != nil {
-				return err
-			}
+		// Enables strict checksums for Maven central if not already configured
+		if !containsMvnCentral(repositories) {
+			repository := maven.NewRepository("https://repo.maven.apache.org/maven2@id=central")
+			repositories = append([]maven.Repository{repository}, repositories...)
+		}
 
-			cm := corev1.ConfigMap{
-				TypeMeta: metav1.TypeMeta{
-					Kind:       "ConfigMap",
-					APIVersion: "v1",
-				},
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      platform.Name + "-maven-settings",
-					Namespace: namespace,
-					Labels: map[string]string{
-						"app": "camel-k",
-					},
+		settings.Profiles = []maven.Profile{
+			{
+				ID: "maven-settings",
+				Activation: maven.Activation{
+					ActiveByDefault: true,
 				},
-				Data: map[string]string{
-					"settings.xml": string(data),
+				Repositories:       repositories,
+				PluginRepositories: repositories,
+			},
+		}
+
+		data, err := util.EncodeXML(settings)
+		if err != nil {
+			return err
+		}
+
+		cm := corev1.ConfigMap{
+			TypeMeta: metav1.TypeMeta{
+				Kind:       "ConfigMap",
+				APIVersion: "v1",
+			},
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      platform.Name + "-maven-settings",
+				Namespace: namespace,
+				Labels: map[string]string{
+					"app": "camel-k",
 				},
-			}
+			},
+			Data: map[string]string{
+				"settings.xml": string(data),
+			},
+		}
 
-			err = install.RuntimeObjectOrCollect(o.Context, c, namespace, collection, &cm)
-			if err != nil {
-				return err
-			}
+		err = install.RuntimeObjectOrCollect(o.Context, c, namespace, collection, &cm)
+		if err != nil {
+			return err
 		}
 
 		if o.mavenSettings != "" {
@@ -434,3 +438,12 @@ func decodeMavenSettings(mavenSettings string) (v1alpha1.ValueSource, error) {
 
 	return v1alpha1.ValueSource{}, fmt.Errorf("illegal maven setting definition, syntax: configmap|secret:resource-name[/settings path]")
 }
+
+func containsMvnCentral(repositories []maven.Repository) bool {
+	for _, r := range repositories {
+		if r.ID == "central" {
+			return true
+		}
+	}
+	return false
+}
diff --git a/pkg/controller/integration/integration_controller.go b/pkg/controller/integration/integration_controller.go
index 5155658d4..b02a450f5 100644
--- a/pkg/controller/integration/integration_controller.go
+++ b/pkg/controller/integration/integration_controller.go
@@ -109,12 +109,45 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
 		return err
 	}
 
-	// Watch for IntegrationPlatform phase transitioning to ready
-	// and enqueue requests for any integrations that are in phase waiting for platform
+	// Watch for IntegrationKit phase transitioning to ready or error and
+	// enqueue requests for any integrations that are in phase waiting for
+	// kit
+	err = c.Watch(&source.Kind{Type: &v1alpha1.IntegrationKit{}}, &handler.EnqueueRequestsFromMapFunc{
+		ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
+			kit := a.Object.(*v1alpha1.IntegrationKit)
+			var requests []reconcile.Request
+
+			if kit.Status.Phase == v1alpha1.IntegrationKitPhaseReady || kit.Status.Phase == v1alpha1.IntegrationKitPhaseError {
+				list := &v1alpha1.IntegrationList{}
+
+				if err := mgr.GetClient().List(context.TODO(), &k8sclient.ListOptions{Namespace: kit.Namespace}, list); err != nil {
+					log.Error(err, "Failed to retrieve integration list")
+					return requests
+				}
+
+				for _, integration := range list.Items {
+					if integration.Status.Phase == v1alpha1.IntegrationPhaseBuildingKit {
+						log.Infof("Kit %s ready, wake-up integration: %s", kit.Name, integration.Name)
+						requests = append(requests, reconcile.Request{
+							NamespacedName: types.NamespacedName{
+								Namespace: integration.Namespace,
+								Name:      integration.Name,
+							},
+						})
+					}
+				}
+			}
+
+			return requests
+		}),
+	})
+
+	// Watch for IntegrationPlatform phase transitioning to ready and enqueue
+	// requests for any integrations that are in phase waiting for platform
 	err = c.Watch(&source.Kind{Type: &v1alpha1.IntegrationPlatform{}}, &handler.EnqueueRequestsFromMapFunc{
 		ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request {
 			platform := a.Object.(*v1alpha1.IntegrationPlatform)
-			requests := []reconcile.Request{}
+			var requests []reconcile.Request
 
 			if platform.Status.Phase == v1alpha1.IntegrationPlatformPhaseReady {
 				list := &v1alpha1.IntegrationList{}
@@ -126,6 +159,7 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
 
 				for _, integration := range list.Items {
 					if integration.Status.Phase == v1alpha1.IntegrationPhaseWaitingForPlatform {
+						log.Infof("Platform %s ready, wake-up integration: %s", platform.Name, integration.Name)
 						requests = append(requests, reconcile.Request{
 							NamespacedName: types.NamespacedName{
 								Namespace: integration.Namespace,
diff --git a/pkg/controller/integrationkit/build.go b/pkg/controller/integrationkit/build.go
index c0df680c4..4ea566431 100644
--- a/pkg/controller/integrationkit/build.go
+++ b/pkg/controller/integrationkit/build.go
@@ -22,17 +22,16 @@ import (
 	"fmt"
 
 	"github.com/apache/camel-k/pkg/util/kubernetes"
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/builder"
+	"github.com/apache/camel-k/pkg/trait"
 
 	k8serrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+
 	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
 
 	"github.com/pkg/errors"
-
-	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-	"github.com/apache/camel-k/pkg/builder"
-	"github.com/apache/camel-k/pkg/trait"
 )
 
 // NewBuildAction creates a new build request handling action for the kit
@@ -175,11 +174,6 @@ func (action *buildAction) handleBuildRunning(ctx context.Context, kit *v1alpha1
 			return err
 		}
 
-		action.L.Info("Inform integrations about kit state change")
-		if err := action.informIntegrations(ctx, target); err != nil {
-			return err
-		}
-
 	case v1alpha1.BuildPhaseError, v1alpha1.BuildPhaseInterrupted:
 		target := kit.DeepCopy()
 
@@ -204,24 +198,3 @@ func (action *buildAction) handleBuildRunning(ctx context.Context, kit *v1alpha1
 
 	return nil
 }
-
-// informIntegrations triggers the processing of all integrations waiting for this kit to be built
-func (action *buildAction) informIntegrations(ctx context.Context, kit *v1alpha1.IntegrationKit) error {
-	list := v1alpha1.NewIntegrationList()
-	err := action.client.List(ctx, &k8sclient.ListOptions{Namespace: kit.Namespace}, &list)
-	if err != nil {
-		return err
-	}
-	for _, integration := range list.Items {
-		integration := integration // pin
-		if integration.Status.Kit != kit.Name {
-			continue
-		}
-		integration.Status.Phase = v1alpha1.IntegrationPhaseResolvingKit
-		err = action.client.Status().Update(ctx, &integration)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/pkg/controller/integrationplatform/initialize.go b/pkg/controller/integrationplatform/initialize.go
index 6976165e8..2fc150b71 100644
--- a/pkg/controller/integrationplatform/initialize.go
+++ b/pkg/controller/integrationplatform/initialize.go
@@ -126,11 +126,35 @@ func (action *initializeAction) Handle(ctx context.Context, ip *v1alpha1.Integra
 	if target.Spec.Build.LocalRepository == "" {
 		target.Spec.Build.LocalRepository = defaults.LocalRepository
 	}
+	if target.Spec.Build.PersistentVolumeClaim == "" {
+		target.Spec.Build.PersistentVolumeClaim = target.Name
+	}
+
+	if target.Spec.Build.Timeout.Duration != 0 {
+		d := target.Spec.Build.Timeout.Duration.Truncate(time.Second)
+
+		if target.Spec.Build.Timeout.Duration != d {
+			action.L.Infof("Build timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Timeout.Duration, d)
+		}
+
+		target.Spec.Build.Timeout.Duration = d
+	}
 	if target.Spec.Build.Timeout.Duration == 0 {
 		target.Spec.Build.Timeout.Duration = 5 * time.Minute
 	}
-	if target.Spec.Build.PersistentVolumeClaim == "" {
-		target.Spec.Build.PersistentVolumeClaim = target.Name
+
+	if target.Spec.Build.Maven.Timeout.Duration != 0 {
+		d := target.Spec.Build.Maven.Timeout.Duration.Truncate(time.Second)
+
+		if target.Spec.Build.Maven.Timeout.Duration != d {
+			action.L.Infof("Maven timeout minimum unit is sec (configured: %s, truncated: %s)", target.Spec.Build.Maven.Timeout.Duration, d)
+		}
+
+		target.Spec.Build.Maven.Timeout.Duration = d
+	}
+	if target.Spec.Build.Maven.Timeout.Duration == 0 {
+		n := target.Spec.Build.Timeout.Duration.Seconds() * 0.75
+		target.Spec.Build.Maven.Timeout.Duration = (time.Duration(n) * time.Second).Truncate(time.Second)
 	}
 
 	action.L.Infof("CamelVersion set to %s", target.Spec.Build.CamelVersion)
@@ -138,6 +162,11 @@ func (action *initializeAction) Handle(ctx context.Context, ip *v1alpha1.Integra
 	action.L.Infof("BaseImage set to %s", target.Spec.Build.BaseImage)
 	action.L.Infof("LocalRepository set to %s", target.Spec.Build.LocalRepository)
 	action.L.Infof("Timeout set to %s", target.Spec.Build.Timeout)
+	action.L.Infof("Maven Timeout set to %s", target.Spec.Build.Maven.Timeout.Duration)
+
+	if target.Spec.Build.Maven.Timeout.Duration != 0 {
+		action.L.Infof("Maven Timeout set to %s", target.Spec.Build.Maven.Timeout.Duration)
+	}
 
 	err = action.client.Update(ctx, target)
 	if err != nil {
diff --git a/pkg/trait/camel.go b/pkg/trait/camel.go
index f5564e999..03e0b254f 100644
--- a/pkg/trait/camel.go
+++ b/pkg/trait/camel.go
@@ -142,7 +142,8 @@ func (t *camelTrait) GenerateCatalog(e *Environment, version string) (*camel.Run
 	}
 
 	mc := maven.NewContext(tmpDir, project)
-	mc.AddArguments(maven.ExtraOptions(e.Platform.Spec.Build.LocalRepository)...)
+	mc.LocalRepository = e.Platform.Spec.Build.LocalRepository
+	mc.Timeout = e.Platform.Spec.Build.Maven.Timeout.Duration
 	mc.AddSystemProperty("catalog.path", tmpDir)
 	mc.AddSystemProperty("catalog.file", "catalog.yaml")
 
@@ -156,7 +157,7 @@ func (t *camelTrait) GenerateCatalog(e *Environment, version string) (*camel.Run
 		return nil, err
 	}
 	if settings != "" {
-		mc.SettingsData = []byte(settings)
+		mc.SettingsContent = []byte(settings)
 	}
 
 	err = maven.Run(mc)
diff --git a/pkg/trait/rest-dsl.go b/pkg/trait/rest-dsl.go
index 6e9778d2e..ab1789d27 100644
--- a/pkg/trait/rest-dsl.go
+++ b/pkg/trait/rest-dsl.go
@@ -113,7 +113,8 @@ func (t *restDslTrait) Apply(e *Environment) error {
 		}
 
 		mc := maven.NewContext(tmpDir, project)
-		mc.AddArguments(maven.ExtraOptions(e.Platform.Spec.Build.LocalRepository)...)
+		mc.LocalRepository = e.Platform.Spec.Build.LocalRepository
+		mc.Timeout = e.Platform.Spec.Build.Maven.Timeout.Duration
 		mc.AddArgument("-Dopenapi.spec=" + in)
 		mc.AddArgument("-Ddsl.out=" + out)
 
@@ -122,7 +123,7 @@ func (t *restDslTrait) Apply(e *Environment) error {
 			return err
 		}
 		if settings != "" {
-			mc.SettingsData = []byte(settings)
+			mc.SettingsContent = []byte(settings)
 		}
 
 		err = maven.Run(mc)
diff --git a/pkg/util/maven/maven.go b/pkg/util/maven/maven.go
index 3e28d1858..757df372c 100644
--- a/pkg/util/maven/maven.go
+++ b/pkg/util/maven/maven.go
@@ -18,9 +18,11 @@ limitations under the License.
 package maven
 
 import (
+	"context"
 	"fmt"
 	"io"
 	"io/ioutil"
+	"math"
 	"os"
 	"os/exec"
 	"path"
@@ -42,12 +44,8 @@ func GenerateProjectStructure(context Context) error {
 		return err
 	}
 
-	if context.SettingsData != nil {
-		if err := util.WriteFileWithContent(context.Path, "settings.xml", context.SettingsData); err != nil {
-			return err
-		}
-	} else if context.Settings != nil {
-		if err := util.WriteFileWithBytesMarshallerContent(context.Path, "settings.xml", context.Settings); err != nil {
+	if context.SettingsContent != nil {
+		if err := util.WriteFileWithContent(context.Path, "settings.xml", context.SettingsContent); err != nil {
 			return err
 		}
 	}
@@ -81,8 +79,8 @@ func GenerateProjectStructure(context Context) error {
 }
 
 // Run --
-func Run(context Context) error {
-	if err := GenerateProjectStructure(context); err != nil {
+func Run(ctx Context) error {
+	if err := GenerateProjectStructure(ctx); err != nil {
 		return err
 	}
 
@@ -91,9 +89,15 @@ func Run(context Context) error {
 		mvnCmd = c
 	}
 
-	args := append(context.AdditionalArguments, "--batch-mode")
+	args := append(ctx.AdditionalArguments, "--batch-mode")
+
+	if ctx.LocalRepository == "" {
+		args = append(ctx.AdditionalArguments, "-Dcamel.noop=true")
+	} else if _, err := os.Stat(ctx.LocalRepository); err == nil {
+		args = append(ctx.AdditionalArguments, "-Dmaven.repo.local="+ctx.LocalRepository)
+	}
 
-	settingsPath := path.Join(context.Path, "settings.xml")
+	settingsPath := path.Join(ctx.Path, "settings.xml")
 	settingsExists, err := util.FileExists(settingsPath)
 	if err != nil {
 		return err
@@ -103,12 +107,20 @@ func Run(context Context) error {
 		args = append(args, "--settings", settingsPath)
 	}
 
-	cmd := exec.Command(mvnCmd, args...)
-	cmd.Dir = context.Path
-	cmd.Stdout = os.Stdout
+	timeout := ctx.Timeout
+	if timeout == 0 {
+		timeout = math.MaxInt64
+	}
+
+	c, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	cmd := exec.CommandContext(c, mvnCmd, args...)
+	cmd.Dir = ctx.Path
 	cmd.Stderr = os.Stderr
+	cmd.Stdout = os.Stdout
 
-	Log.Infof("execute: %s", strings.Join(cmd.Args, " "))
+	Log.WithValues("timeout", timeout.String()).Infof("executing: %s", strings.Join(cmd.Args, " "))
 
 	return cmd.Run()
 }
@@ -150,11 +162,3 @@ func ParseGAV(gav string) (Dependency, error) {
 
 	return dep, nil
 }
-
-// ExtraOptions --
-func ExtraOptions(localRepo string) []string {
-	if _, err := os.Stat(localRepo); err == nil {
-		return []string{"-Dmaven.repo.local=" + localRepo}
-	}
-	return []string{"-Dcamel.noop=true"}
-}
diff --git a/pkg/util/maven/maven_project.go b/pkg/util/maven/maven_project.go
index 541414f00..db4ea0441 100644
--- a/pkg/util/maven/maven_project.go
+++ b/pkg/util/maven/maven_project.go
@@ -166,10 +166,12 @@ func NewRepository(repo string) Repository {
 	r := Repository{
 		URL: repo,
 		Releases: RepositoryPolicy{
-			Enabled: true,
+			Enabled:        true,
+			ChecksumPolicy: "fail",
 		},
 		Snapshots: RepositoryPolicy{
-			Enabled: false,
+			Enabled:        false,
+			ChecksumPolicy: "fail",
 		},
 	}
 
@@ -184,6 +186,9 @@ func NewRepository(repo string) Repository {
 				r.Releases.Enabled = false
 			case strings.HasPrefix(attribute, "id="):
 				r.ID = attribute[3:]
+			case strings.HasPrefix(attribute, "checksumpolicy="):
+				r.Snapshots.ChecksumPolicy = attribute[15:]
+				r.Releases.ChecksumPolicy = attribute[15:]
 			}
 		}
 	}
diff --git a/pkg/util/maven/maven_types.go b/pkg/util/maven/maven_types.go
index c292b13b0..6adbd0636 100644
--- a/pkg/util/maven/maven_types.go
+++ b/pkg/util/maven/maven_types.go
@@ -21,6 +21,7 @@ import (
 	"bytes"
 	"encoding/xml"
 	"fmt"
+	"time"
 )
 
 // Repository --
@@ -34,8 +35,9 @@ type Repository struct {
 
 // RepositoryPolicy --
 type RepositoryPolicy struct {
-	Enabled      bool   `xml:"enabled"`
-	UpdatePolicy string `xml:"updatePolicy,omitempty"`
+	Enabled        bool   `xml:"enabled"`
+	UpdatePolicy   string `xml:"updatePolicy,omitempty"`
+	ChecksumPolicy string `xml:"checksumPolicy,omitempty"`
 }
 
 // Build --
@@ -102,10 +104,11 @@ func NewContext(buildDir string, project Project) Context {
 type Context struct {
 	Path                string
 	Project             Project
-	Settings            *Settings
-	SettingsData        []byte
+	SettingsContent     []byte
 	AdditionalArguments []string
 	AdditionalEntries   map[string]interface{}
+	Timeout             time.Duration
+	LocalRepository     string
 }
 
 // AddEntry --


Test patch:
diff --git a/pkg/controller/integrationplatform/initialize_test.go b/pkg/controller/integrationplatform/initialize_test.go
new file mode 100644
index 000000000..8260f3a1a
--- /dev/null
+++ b/pkg/controller/integrationplatform/initialize_test.go
@@ -0,0 +1,149 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package integrationplatform
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/util/log"
+	"github.com/apache/camel-k/pkg/util/test"
+	"github.com/stretchr/testify/assert"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func TestTimeouts_Default(t *testing.T) {
+	ip := v1alpha1.IntegrationPlatform{}
+	ip.Namespace = "ns"
+	ip.Name = "camel-k"
+	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
+
+	c, err := test.NewFakeClient(&ip)
+	assert.Nil(t, err)
+
+	h := NewInitializeAction()
+	h.InjectLogger(log.Log)
+	h.InjectClient(c)
+
+	err = h.Handle(context.TODO(), &ip)
+	assert.Nil(t, err)
+
+	key := k8sclient.ObjectKey{
+		Name:      ip.Name,
+		Namespace: ip.Namespace,
+	}
+
+	answer := v1alpha1.NewIntegrationPlatform(ip.Name, ip.Namespace)
+
+	err = c.Get(context.TODO(), key, &answer)
+	assert.Nil(t, err)
+
+	n := answer.Spec.Build.Timeout.Duration.Seconds() * 0.75
+	d := (time.Duration(n) * time.Second).Truncate(time.Second)
+
+	assert.Equal(t, d, answer.Spec.Build.Maven.Timeout.Duration)
+	assert.Equal(t, 5*time.Minute, answer.Spec.Build.Timeout.Duration)
+}
+
+func TestTimeouts_MavenComputedFromBuild(t *testing.T) {
+	ip := v1alpha1.IntegrationPlatform{}
+	ip.Namespace = "ns"
+	ip.Name = "camel-k"
+	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
+
+	timeout, err := time.ParseDuration("1m1ms")
+	assert.Nil(t, err)
+
+	ip.Spec.Build.Timeout = metav1.Duration{
+		Duration: timeout,
+	}
+
+	c, err := test.NewFakeClient(&ip)
+	assert.Nil(t, err)
+
+	h := NewInitializeAction()
+	h.InjectLogger(log.Log)
+	h.InjectClient(c)
+
+	err = h.Handle(context.TODO(), &ip)
+	assert.Nil(t, err)
+
+	key := k8sclient.ObjectKey{
+		Name:      ip.Name,
+		Namespace: ip.Namespace,
+	}
+
+	answer := v1alpha1.NewIntegrationPlatform(ip.Name, ip.Namespace)
+
+	err = c.Get(context.TODO(), key, &answer)
+	assert.Nil(t, err)
+
+	n := answer.Spec.Build.Timeout.Duration.Seconds() * 0.75
+	d := (time.Duration(n) * time.Second).Truncate(time.Second)
+
+	assert.Equal(t, d, answer.Spec.Build.Maven.Timeout.Duration)
+	assert.Equal(t, 1*time.Minute, answer.Spec.Build.Timeout.Duration)
+}
+
+func TestTimeouts_Truncated(t *testing.T) {
+	ip := v1alpha1.IntegrationPlatform{}
+	ip.Namespace = "ns"
+	ip.Name = "camel-k"
+	ip.Spec.Cluster = v1alpha1.IntegrationPlatformClusterOpenShift
+
+	bt, err := time.ParseDuration("5m1ms")
+	assert.Nil(t, err)
+
+	ip.Spec.Build.Timeout = metav1.Duration{
+		Duration: bt,
+	}
+
+	mt, err := time.ParseDuration("2m1ms")
+	assert.Nil(t, err)
+
+	ip.Spec.Build.Maven.Timeout = metav1.Duration{
+		Duration: mt,
+	}
+
+	c, err := test.NewFakeClient(&ip)
+	assert.Nil(t, err)
+
+	h := NewInitializeAction()
+	h.InjectLogger(log.Log)
+	h.InjectClient(c)
+
+	err = h.Handle(context.TODO(), &ip)
+	assert.Nil(t, err)
+
+	key := k8sclient.ObjectKey{
+		Name:      ip.Name,
+		Namespace: ip.Namespace,
+	}
+
+	answer := v1alpha1.NewIntegrationPlatform(ip.Name, ip.Namespace)
+
+	err = c.Get(context.TODO(), key, &answer)
+	assert.Nil(t, err)
+
+	assert.Equal(t, time.Duration(2*time.Minute), answer.Spec.Build.Maven.Timeout.Duration)
+	assert.Equal(t, time.Duration(5*time.Minute), answer.Spec.Build.Timeout.Duration)
+}
diff --git a/pkg/util/maven/maven_project_test.go b/pkg/util/maven/maven_project_test.go
index 6c8e980e0..d717d7fa0 100644
--- a/pkg/util/maven/maven_project_test.go
+++ b/pkg/util/maven/maven_project_test.go
@@ -187,6 +187,8 @@ func TestNewRepository(t *testing.T) {
 	assert.Equal(t, "http://nexus/public", r.URL)
 	assert.True(t, r.Releases.Enabled)
 	assert.False(t, r.Snapshots.Enabled)
+	assert.Equal(t, "fail", r.Releases.ChecksumPolicy)
+	assert.Equal(t, "fail", r.Snapshots.ChecksumPolicy)
 }
 
 func TestNewRepositoryWithSnapshots(t *testing.T) {
@@ -195,6 +197,8 @@ func TestNewRepositoryWithSnapshots(t *testing.T) {
 	assert.Equal(t, "http://nexus/public", r.URL)
 	assert.True(t, r.Releases.Enabled)
 	assert.True(t, r.Snapshots.Enabled)
+	assert.Equal(t, "fail", r.Releases.ChecksumPolicy)
+	assert.Equal(t, "fail", r.Snapshots.ChecksumPolicy)
 }
 
 func TestNewRepositoryWithSnapshotsAndID(t *testing.T) {
@@ -203,6 +207,8 @@ func TestNewRepositoryWithSnapshotsAndID(t *testing.T) {
 	assert.Equal(t, "http://nexus/public", r.URL)
 	assert.True(t, r.Releases.Enabled)
 	assert.True(t, r.Snapshots.Enabled)
+	assert.Equal(t, "fail", r.Releases.ChecksumPolicy)
+	assert.Equal(t, "fail", r.Snapshots.ChecksumPolicy)
 }
 
 func TestNewRepositoryWithID(t *testing.T) {
@@ -211,4 +217,16 @@ func TestNewRepositoryWithID(t *testing.T) {
 	assert.Equal(t, "http://nexus/public", r.URL)
 	assert.True(t, r.Releases.Enabled)
 	assert.False(t, r.Snapshots.Enabled)
+	assert.Equal(t, "fail", r.Releases.ChecksumPolicy)
+	assert.Equal(t, "fail", r.Snapshots.ChecksumPolicy)
+}
+
+func TestNewRepositoryWithChecksumPolicy(t *testing.T) {
+	r := NewRepository("http://nexus/public@checksumpolicy=warn")
+	assert.Equal(t, "", r.ID)
+	assert.Equal(t, "http://nexus/public", r.URL)
+	assert.True(t, r.Releases.Enabled)
+	assert.False(t, r.Snapshots.Enabled)
+	assert.Equal(t, "warn", r.Releases.ChecksumPolicy)
+	assert.Equal(t, "warn", r.Snapshots.ChecksumPolicy)
 }
diff --git a/pkg/util/maven/maven_settings_test.go b/pkg/util/maven/maven_settings_test.go
index ef1df7f06..75d2a208f 100644
--- a/pkg/util/maven/maven_settings_test.go
+++ b/pkg/util/maven/maven_settings_test.go
@@ -40,10 +40,12 @@ const expectedSettings = `<?xml version="1.0" encoding="UTF-8"?>
           <url>https://repo.maven.apache.org/maven2</url>
           <snapshots>
             <enabled>false</enabled>
+            <checksumPolicy>warn</checksumPolicy>
           </snapshots>
           <releases>
             <enabled>true</enabled>
             <updatePolicy>never</updatePolicy>
+            <checksumPolicy>fail</checksumPolicy>
           </releases>
         </repository>
       </repositories>
@@ -66,11 +68,13 @@ func TestSettingsGeneration(t *testing.T) {
 					ID:  "central",
 					URL: "https://repo.maven.apache.org/maven2",
 					Snapshots: RepositoryPolicy{
-						Enabled: false,
+						Enabled:        false,
+						ChecksumPolicy: "warn",
 					},
 					Releases: RepositoryPolicy{
-						Enabled:      true,
-						UpdatePolicy: "never",
+						Enabled:        true,
+						UpdatePolicy:   "never",
+						ChecksumPolicy: "fail",
 					},
 				},
 			},