Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): store executed traits in status #5851

Merged
merged 2 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 36 additions & 46 deletions addons/master/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type Trait struct {
type masterTrait struct {
trait.BaseTrait
Trait `property:",squash"`
delegateDependencies []string `json:"-"`
delegateDependencies []string
}

// NewMasterTrait --.
Expand Down Expand Up @@ -105,7 +105,7 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi
}
}
if found {
if t.IncludeDelegateDependencies == nil || *t.IncludeDelegateDependencies {
if ptr.Deref(t.IncludeDelegateDependencies, true) {
t.delegateDependencies = findAdditionalDependencies(e, meta)
}
}
Expand All @@ -117,28 +117,17 @@ func (t *masterTrait) Configure(e *trait.Environment) (bool, *trait.TraitConditi
return false, nil, err
}
if enabled {
t.Enabled = ptr.To(enabled)
if t.ResourceName == nil {
val := e.Integration.Name + "-lock"
t.ResourceName = &val
}

if t.ResourceType == nil {
t.ResourceType = ptr.To(leaseResourceType)
}

if t.LabelKey == nil {
val := v1.IntegrationLabel
t.LabelKey = &val
}

if t.LabelValue == nil {
t.LabelValue = &e.Integration.Name
}

return true, nil, nil
}

return false, nil, nil
return enabled, nil, nil
}

func (t *masterTrait) Apply(e *trait.Environment) error {
Expand Down Expand Up @@ -172,49 +161,50 @@ func (t *masterTrait) setCustomizerConfiguration(e *trait.Environment) {
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.enabled=true"},
)
if t.ResourceName != nil {
resourceName := t.ResourceName
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.kubernetesResourceNames=" + *resourceName},
)
}
if t.ResourceType != nil {
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.leaseResourceType" + *t.ResourceType},
)
}
if t.LabelKey != nil {
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelKey=" + *t.LabelKey},
)
}
if t.LabelValue != nil {
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelValue=" + *t.LabelValue},
)
}
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.kubernetesResourceNames=" + *t.ResourceName},
)
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.leaseResourceType" + t.getResourceKey()},
)
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelKey=" + t.getLabelKey()},
)
e.Integration.Status.Configuration = append(e.Integration.Status.Configuration,
v1.ConfigurationSpec{Type: "property", Value: "customizer.master.labelValue=" + *t.LabelValue},
)
}

func (t *masterTrait) setCatalogConfiguration(e *trait.Environment) {
if e.ApplicationProperties == nil {
e.ApplicationProperties = make(map[string]string)
}
if t.ResourceName != nil {
e.ApplicationProperties["camel.k.master.resourceName"] = *t.ResourceName
}
if t.ResourceType != nil {
e.ApplicationProperties["camel.k.master.resourceType"] = *t.ResourceType
}
if t.LabelKey != nil && t.LabelValue != nil {
e.ApplicationProperties["camel.k.master.labelKey"] = *t.LabelKey
e.ApplicationProperties["camel.k.master.labelValue"] = *t.LabelValue
}
e.ApplicationProperties["camel.k.master.resourceName"] = *t.ResourceName
e.ApplicationProperties["camel.k.master.resourceType"] = t.getResourceKey()
e.ApplicationProperties["camel.k.master.labelKey"] = t.getLabelKey()
e.ApplicationProperties["camel.k.master.labelValue"] = *t.LabelValue

for _, cp := range e.CamelCatalog.Runtime.Capabilities["master"].RuntimeProperties {
e.ApplicationProperties[trait.CapabilityPropertyKey(cp.Key, e.ApplicationProperties)] = cp.Value
}
}

func (t *masterTrait) getResourceKey() string {
if t.ResourceType == nil {
return leaseResourceType
}

return *t.ResourceType
}

func (t *masterTrait) getLabelKey() string {
if t.LabelKey == nil {
return v1.IntegrationLabel
}

return *t.LabelKey
}

func findAdditionalDependencies(e *trait.Environment, meta metadata.IntegrationMetadata) []string {
var dependencies []string
for _, endpoint := range meta.FromURIs {
Expand Down
78 changes: 78 additions & 0 deletions addons/master/master_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"testing"

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"
"github.com/apache/camel-k/v2/pkg/util/camel"
"github.com/apache/camel-k/v2/pkg/util/kubernetes"
Expand All @@ -29,6 +30,7 @@ import (
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
)

func TestMasterOn(t *testing.T) {
Expand Down Expand Up @@ -184,3 +186,79 @@ func TestMasterOff(t *testing.T) {
assert.Empty(t, conditions)
assert.False(t, configured)
}

func TestMasterAuto(t *testing.T) {
catalog, err := camel.DefaultCatalog()
require.NoError(t, err)

client, err := test.NewFakeClient()
require.NoError(t, err)
traitCatalog := trait.NewCatalog(nil)

environment := trait.Environment{
CamelCatalog: catalog,
Catalog: traitCatalog,
Client: client,
Integration: &v1.Integration{
ObjectMeta: metav1.ObjectMeta{
Name: "test",
Namespace: "ns",
},
Status: v1.IntegrationStatus{
Phase: v1.IntegrationPhaseInitialization,
},
Spec: v1.IntegrationSpec{
Profile: v1.TraitProfileKnative,
Sources: []v1.SourceSpec{
{
DataSpec: v1.DataSpec{
Name: "Master.java",
Content: `from("master:lock:timer:tick").to("log:test")`,
},
Language: v1.LanguageJavaSource,
},
},
},
},
Platform: &v1.IntegrationPlatform{
Spec: v1.IntegrationPlatformSpec{
Cluster: v1.IntegrationPlatformClusterOpenShift,
Build: v1.IntegrationPlatformBuildSpec{
PublishStrategy: v1.IntegrationPlatformBuildPublishStrategyS2I,
Registry: v1.RegistrySpec{Address: "registry"},
RuntimeVersion: catalog.Runtime.Version,
},
Profile: v1.TraitProfileKnative,
},
Status: v1.IntegrationPlatformStatus{
Phase: v1.IntegrationPlatformPhaseReady,
},
},
EnvVars: make([]corev1.EnvVar, 0),
ExecutedTraits: make([]trait.Trait, 0),
Resources: kubernetes.NewCollection(),
}
environment.Platform.ResyncStatusFullConfig()

mt := NewMasterTrait()
mt.InjectClient(client)
trait, _ := mt.(*masterTrait)
// Initialization phase
configured, conditions, err := trait.Configure(&environment)
require.NoError(t, err)
assert.Empty(t, conditions)
assert.True(t, configured)
err = trait.Apply(&environment)
require.NoError(t, err)

expectedTrait := &masterTrait{
Trait: Trait{
Trait: traitv1.Trait{
Enabled: ptr.To(true),
},
ResourceName: ptr.To("test-lock"),
LabelValue: ptr.To("test"),
},
}
assert.Equal(t, expectedTrait.Trait, trait.Trait)
}
50 changes: 22 additions & 28 deletions addons/resume/resume.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ package resume
import (
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/metadata"
"github.com/apache/camel-k/v2/pkg/trait"
"github.com/apache/camel-k/v2/pkg/util"
"github.com/apache/camel-k/v2/pkg/util/log"
"k8s.io/utils/ptr"
)

Expand All @@ -46,6 +44,7 @@ import (
type Trait struct {
traitv1.Trait `property:",squash"`
// Enables automatic configuration of the trait.
// Deprecated: not in use.
Auto *bool `property:"auto" json:"auto,omitempty"`
// The type of the resume strategy to use
ResumeStrategy string `property:"resume-strategy,omitempty"`
Expand All @@ -64,8 +63,8 @@ type resumeTrait struct {
}

const (
KafkaSingle = "org.apache.camel.processor.resume.kafka.SingleNodeKafkaResumeStrategy"
StrategyPath = "camel-k-offsets"
kafkaSingle = "org.apache.camel.processor.resume.kafka.SingleNodeKafkaResumeStrategy"
strategyPath = "camel-k-offsets"
)

func NewResumeTrait() trait.Trait {
Expand All @@ -82,28 +81,7 @@ func (r *resumeTrait) Configure(environment *trait.Environment) (bool, *trait.Tr
return false, nil, nil
}

if ptr.Deref(r.Auto, true) {
_, err := environment.ConsumeMeta(func(meta metadata.IntegrationMetadata) bool {
for _, endpoint := range meta.FromURIs {
log.Infof("Processing component %s", endpoint)
}

return true
})
if err != nil {
return false, nil, err
}

if r.ResumeStrategy == "" {
r.ResumeStrategy = KafkaSingle
}

if r.ResumePath == "" {
r.ResumePath = StrategyPath
}
}

return r.Enabled != nil && *r.Enabled, nil, nil
return ptr.Deref(r.Enabled, false), nil, nil
}

func (r *resumeTrait) Apply(environment *trait.Environment) error {
Expand All @@ -113,11 +91,27 @@ func (r *resumeTrait) Apply(environment *trait.Environment) error {

if environment.IntegrationInRunningPhases() {
environment.ApplicationProperties["customizer.resume.enabled"] = "true"
environment.ApplicationProperties["customizer.resume.resumeStrategy"] = r.ResumeStrategy
environment.ApplicationProperties["customizer.resume.resumePath"] = r.ResumePath
environment.ApplicationProperties["customizer.resume.resumeStrategy"] = r.getResumeStrategy()
environment.ApplicationProperties["customizer.resume.resumePath"] = r.getResumePath()
environment.ApplicationProperties["customizer.resume.resumeServer"] = r.ResumeServer
environment.ApplicationProperties["customizer.resume.cacheFillPolicy"] = r.CacheFillPolicy
}

return nil
}

func (r *resumeTrait) getResumeStrategy() string {
if r.ResumeStrategy == "" {
return kafkaSingle
}

return r.ResumeStrategy
}

func (r *resumeTrait) getResumePath() string {
if r.ResumePath == "" {
return strategyPath
}

return r.ResumePath
}
13 changes: 13 additions & 0 deletions docs/modules/ROOT/pages/pipes/promoting.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[[promoting-pipes]]
= Promoting Pipes across environments

As soon as you have an Pipes running in your cluster, you will be challenged to move that Pipe to an higher environment. Ie, you can test your Pipe in a **development** environment, and, as soon as you're happy with the result, you will need to move it into a **production** environment.

[[cli-promote]]
== CLI `promote` command

You may already be familiar with this command as seen when xref:running/promoting.adoc[promoting Integrations across environments]. The command is smart enough to detect when you want to promote a Pipe or an Integration and it works exactly in the same manner.
Expand Down Expand Up @@ -49,3 +51,14 @@ status: {}
```

As you may already have seen with the Integration example, also here the Pipe is reusing the very same container image. From a release perspective we are guaranteeing the **immutability** of the Pipe as the container used is exactly the same of the one we have tested in development (what we change are just the configurations, if any).

[[traits]]
== Moving traits

NOTE: this feature is available starting from version 2.5

When you use the `promote` subcommand, you're also keeping the status of any configured trait along with the new promoted Pipe. The tool is in fact in charge to recover the trait configuration of the source Pipe and port it over to the new Pipe promoted.

This is particularly nice when you have certain traits which are requiring the scan the source code (for instance, Service trait). In this way, when you promote the new Pipe, the traits will be automatically configured to copy any parameter, replicating the very exact behavior between the source and destination environment.

With this approach, you won't need to worry any longer about any trait which was requiring the source to be attached in order to automatically scan for features.
12 changes: 12 additions & 0 deletions docs/modules/ROOT/pages/running/promoting.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

As soon as you have an Integration running in your cluster, you will be challenged to move that Integration to an higher environment. Ie, you can test your Integration in a **development** environment, and, as soon as you're happy with the result, you will need to move it into a **production** environment.

[[cli-promote]]
== CLI `promote` command

Camel K has an opinionated way to achieve the promotion goal through the usage of `kamel promote` command. With this command you will be able to easily move an Integration from one namespace to another without worrying about any low level detail such as resources needed by the Integration. You only need to make sure that both the source operator and the destination operator are using the same container registry and that the destination namespace provides the required Configmaps, Secrets or Kamelets required by the Integration.
Expand Down Expand Up @@ -52,3 +53,14 @@ hello, I am production!
Something nice is that since the Integration is reusing the very same container image, the execution of the new application will be immediate. Also from a release perspective we are guaranteeing the **immutability** of the Integration as the container used is exactly the same of the one we have tested in development (what we change are just the configurations).

Please notice that the Integration running in test is not altered in any way and will be running until any user will stop it.

[[traits]]
== Moving traits

NOTE: this feature is available starting from version 2.5

When you use the `promote` subcommand, you're also keeping the status of any configured trait along with the new promoted Integration. The tool is in fact in charge to recover the trait configuration of the source Integration and port it over to the new Integration promoted.

This is particularly nice when you have certain traits which are requiring the scan the source code (for instance, Service trait). In this way, when you promote the new Integration, the traits will be automatically configured to copy any parameter, replicating the very exact behavior between the source and destination environment.

With this approach, you won't need to worry any longer about any trait which was requiring the source to be attached in order to automatically scan for features.
8 changes: 8 additions & 0 deletions docs/modules/ROOT/partials/apis/camel-k-crds.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3533,6 +3533,13 @@ a list of dependencies needed by the application

the profile needed to run this Integration

|`traits` +
*xref:#_camel_apache_org_v1_Traits[Traits]*
|


the traits executed for the Integration

|`integrationKit` +
*https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectreference-v1-core[Kubernetes core/v1.ObjectReference]*
|
Expand Down Expand Up @@ -5817,6 +5824,7 @@ TraitConfiguration parameters configuration
* <<#_camel_apache_org_v1_IntegrationPlatformSpec, IntegrationPlatformSpec>>
* <<#_camel_apache_org_v1_IntegrationProfileSpec, IntegrationProfileSpec>>
* <<#_camel_apache_org_v1_IntegrationSpec, IntegrationSpec>>
* <<#_camel_apache_org_v1_IntegrationStatus, IntegrationStatus>>

Traits represents the collection of trait configurations.

Expand Down
1 change: 1 addition & 0 deletions docs/modules/traits/pages/resume.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ The following configuration options are available:
| resume.auto
| bool
| Enables automatic configuration of the trait.
Deprecated: not in use.

| resume.resume-strategy,omitempty
| string
Expand Down
Loading
Loading