This is an automated email from the ASF dual-hosted git repository. nferraro pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 7e4d6619da416c1e8c20e476055c33d2b14b1725 Author: Nicola Ferraro <[email protected]> AuthorDate: Tue Jun 23 11:19:01 2020 +0200 kamelets: initial structure --- .../csv-config.yaml => crd-kamelet.yaml} | 38 +++-- ...el-k.v1.1.0-snapshot.clusterserviceversion.yaml | 5 + .../kamelets.camel.apache.org.crd.yaml} | 38 +++-- deploy/olm-catalog/csv-config.yaml | 1 + .../camel-k/crds/crd-kamelet.yaml | 38 +++-- pkg/apis/addtoscheme_camel_v1alpha1.go | 27 +++ pkg/apis/camel/v1alpha1/kamelet_types.go | 105 ++++++++++++ pkg/apis/camel/v1alpha1/kamelet_types_support.go | 46 +++++ pkg/apis/camel/v1alpha1/register.go | 55 ++++++ pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go | 102 +++++++++++ pkg/controller/kamelet/action.go | 54 ++++++ pkg/controller/kamelet/initialize.go | 46 +++++ pkg/controller/kamelet/kamelet_controller.go | 187 +++++++++++++++++++++ pkg/controller/kamelet/log.go | 23 +++ pkg/event/manager.go | 37 ++++ pkg/install/cluster.go | 39 +++-- pkg/util/log/log.go | 11 ++ 17 files changed, 808 insertions(+), 44 deletions(-) diff --git a/deploy/olm-catalog/csv-config.yaml b/deploy/crd-kamelet.yaml similarity index 62% copy from deploy/olm-catalog/csv-config.yaml copy to deploy/crd-kamelet.yaml index 3075a7a..7d36aab 100644 --- a/deploy/olm-catalog/csv-config.yaml +++ b/deploy/crd-kamelet.yaml @@ -15,13 +15,31 @@ # limitations under the License. # --------------------------------------------------------------------------- -operator-path: deploy/operator-deployment.yaml -crd-cr-paths: - - deploy/crd-build.yaml - - deploy/crd-camel-catalog.yaml - - deploy/crd-integration.yaml - - deploy/crd-integration-kit.yaml - - deploy/crd-integration-platform.yaml -role-paths: - - deploy/operator-role-olm.yaml - - deploy/operator-role-olm-cluster.yaml +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: kamelets.camel.apache.org + labels: + app: "camel-k" +spec: + group: camel.apache.org + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + names: + kind: Kamelet + listKind: KameletList + plural: kamelets + singular: kamelet + shortNames: + - kl + additionalPrinterColumns: + - name: Phase + type: string + description: The Kamelet phase + JSONPath: .status.phase diff --git a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml index d761fee..4cc2a53 100644 --- a/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml +++ b/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/camel-k.v1.1.0-snapshot.clusterserviceversion.yaml @@ -122,6 +122,11 @@ spec: kind: IntegrationPlatform name: integrationplatforms.camel.apache.org version: v1 + - description: A Camel K Kamelet resource + displayName: Kamelet + kind: Kamelet + name: kamelets.camel.apache.org + version: v1alpha1 description: | Apache Camel K ============== diff --git a/deploy/olm-catalog/csv-config.yaml b/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml similarity index 62% copy from deploy/olm-catalog/csv-config.yaml copy to deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml index 3075a7a..7d36aab 100644 --- a/deploy/olm-catalog/csv-config.yaml +++ b/deploy/olm-catalog/camel-k-dev/1.1.0-snapshot/kamelets.camel.apache.org.crd.yaml @@ -15,13 +15,31 @@ # limitations under the License. # --------------------------------------------------------------------------- -operator-path: deploy/operator-deployment.yaml -crd-cr-paths: - - deploy/crd-build.yaml - - deploy/crd-camel-catalog.yaml - - deploy/crd-integration.yaml - - deploy/crd-integration-kit.yaml - - deploy/crd-integration-platform.yaml -role-paths: - - deploy/operator-role-olm.yaml - - deploy/operator-role-olm-cluster.yaml +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: kamelets.camel.apache.org + labels: + app: "camel-k" +spec: + group: camel.apache.org + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + names: + kind: Kamelet + listKind: KameletList + plural: kamelets + singular: kamelet + shortNames: + - kl + additionalPrinterColumns: + - name: Phase + type: string + description: The Kamelet phase + JSONPath: .status.phase diff --git a/deploy/olm-catalog/csv-config.yaml b/deploy/olm-catalog/csv-config.yaml index 3075a7a..a5e2dd2 100644 --- a/deploy/olm-catalog/csv-config.yaml +++ b/deploy/olm-catalog/csv-config.yaml @@ -22,6 +22,7 @@ crd-cr-paths: - deploy/crd-integration.yaml - deploy/crd-integration-kit.yaml - deploy/crd-integration-platform.yaml + - deploy/crd-kamelet.yaml role-paths: - deploy/operator-role-olm.yaml - deploy/operator-role-olm-cluster.yaml diff --git a/deploy/olm-catalog/csv-config.yaml b/helm/camel-k/crds/crd-kamelet.yaml similarity index 62% copy from deploy/olm-catalog/csv-config.yaml copy to helm/camel-k/crds/crd-kamelet.yaml index 3075a7a..7d36aab 100644 --- a/deploy/olm-catalog/csv-config.yaml +++ b/helm/camel-k/crds/crd-kamelet.yaml @@ -15,13 +15,31 @@ # limitations under the License. # --------------------------------------------------------------------------- -operator-path: deploy/operator-deployment.yaml -crd-cr-paths: - - deploy/crd-build.yaml - - deploy/crd-camel-catalog.yaml - - deploy/crd-integration.yaml - - deploy/crd-integration-kit.yaml - - deploy/crd-integration-platform.yaml -role-paths: - - deploy/operator-role-olm.yaml - - deploy/operator-role-olm-cluster.yaml +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: kamelets.camel.apache.org + labels: + app: "camel-k" +spec: + group: camel.apache.org + scope: Namespaced + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + names: + kind: Kamelet + listKind: KameletList + plural: kamelets + singular: kamelet + shortNames: + - kl + additionalPrinterColumns: + - name: Phase + type: string + description: The Kamelet phase + JSONPath: .status.phase diff --git a/pkg/apis/addtoscheme_camel_v1alpha1.go b/pkg/apis/addtoscheme_camel_v1alpha1.go new file mode 100644 index 0000000..001d778 --- /dev/null +++ b/pkg/apis/addtoscheme_camel_v1alpha1.go @@ -0,0 +1,27 @@ +/* +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 apis + +import ( + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) +} diff --git a/pkg/apis/camel/v1alpha1/kamelet_types.go b/pkg/apis/camel/v1alpha1/kamelet_types.go new file mode 100644 index 0000000..ed99b66 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/kamelet_types.go @@ -0,0 +1,105 @@ +package v1alpha1 + +import ( + camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1" + openapi "github.com/go-openapi/spec" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// KameletSpec defines the desired state of Kamelet +type KameletSpec struct { + Info KameletInfo `json:"info,omitempty"` + Parameters []KameletParameter `json:"parameters,omitempty"` + Authorization AuthorizationSpec `json:"authorization,omitempty"` + Sources *camelv1.SourceSpec `json:"sources,omitempty"` + Flow *camelv1.Flow `json:"flow,omitempty"` + Dependencies []string `json:"dependencies,omitempty"` +} + +type KameletInfo struct { + DisplayName string `json:"displayName,omitempty"` + Description string `json:"description,omitempty"` + Group string `json:"group,omitempty"` + Icon KameletIcon `json:"icon,omitempty"` +} + +type KameletIcon struct { + Data string `json:"data,omitempty"` + MediaType string `json:"mediaType,omitempty"` +} + +type KameletParameter struct { + Name string `json:"name,omitempty"` + Required bool `json:"required,omitempty"` + Description string `json:"description,omitempty"` + Annotations map[string]string `json:"annotations,omitempty"` + Schema *openapi.Schema `json:"schema,omitempty"` +} + +// AuthorizationSpec is TODO (oauth information) +type AuthorizationSpec struct { +} + +// KameletStatus defines the observed state of Kamelet +type KameletStatus struct { + Phase KameletPhase `json:"phase,omitempty"` + Conditions []KameletCondition `json:"conditions,omitempty"` +} + +// KameletCondition describes the state of a resource at a certain point. +type KameletCondition struct { + // Type of kamelet condition. + Type KameletConditionType `json:"type"` + // Status of the condition, one of True, False, Unknown. + Status corev1.ConditionStatus `json:"status"` + // The last time this condition was updated. + LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"` + // Last time the condition transitioned from one status to another. + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` + // The reason for the condition's last transition. + Reason string `json:"reason,omitempty"` + // A human readable message indicating details about the transition. + Message string `json:"message,omitempty"` +} + +type KameletConditionType string + +const ( + // KameletConditionReady -- + KameletConditionReady KameletConditionType = "Ready" +) + +type KameletPhase string + +const ( + // KameletKind -- + KameletKind string = "Kamelet" + + // KameletPhaseNone -- + KameletPhaseNone KameletPhase = "" + // KameletPhaseReady -- + KameletPhaseReady KameletPhase = "Ready" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Kamelet is the Schema for the kamelets API +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=kamelets,scope=Namespaced +type Kamelet struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KameletSpec `json:"spec,omitempty"` + Status KameletStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// KameletList contains a list of Kamelet +type KameletList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Kamelet `json:"items"` +} diff --git a/pkg/apis/camel/v1alpha1/kamelet_types_support.go b/pkg/apis/camel/v1alpha1/kamelet_types_support.go new file mode 100644 index 0000000..eb18093 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/kamelet_types_support.go @@ -0,0 +1,46 @@ +package v1alpha1 + +import ( + v1 "github.com/apache/camel-k/pkg/apis/camel/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GetConditions -- +func (in *KameletStatus) GetConditions() []v1.ResourceCondition { + res := make([]v1.ResourceCondition, 0, len(in.Conditions)) + for _, c := range in.Conditions { + res = append(res, c) + } + return res +} + +// GetType -- +func (c KameletCondition) GetType() string { + return string(c.Type) +} + +// GetStatus -- +func (c KameletCondition) GetStatus() corev1.ConditionStatus { + return c.Status +} + +// GetLastUpdateTime -- +func (c KameletCondition) GetLastUpdateTime() metav1.Time { + return c.LastUpdateTime +} + +// GetLastTransitionTime -- +func (c KameletCondition) GetLastTransitionTime() metav1.Time { + return c.LastTransitionTime +} + +// GetReason -- +func (c KameletCondition) GetReason() string { + return c.Reason +} + +// GetMessage -- +func (c KameletCondition) GetMessage() string { + return c.Message +} diff --git a/pkg/apis/camel/v1alpha1/register.go b/pkg/apis/camel/v1alpha1/register.go new file mode 100644 index 0000000..5bafade --- /dev/null +++ b/pkg/apis/camel/v1alpha1/register.go @@ -0,0 +1,55 @@ +/* +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. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains Camel unstable API Schema definitions +// +k8s:deepcopy-gen=package,register +// +groupName=camel.apache.org +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "camel.apache.org", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme is a shortcut to SchemeBuilder.AddToScheme + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Kamelet{}, + &KameletList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..8c7d0d7 --- /dev/null +++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,102 @@ +// +build !ignore_autogenerated + +// Code generated by operator-sdk. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Kamelet) DeepCopyInto(out *Kamelet) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kamelet. +func (in *Kamelet) DeepCopy() *Kamelet { + if in == nil { + return nil + } + out := new(Kamelet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Kamelet) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletList) DeepCopyInto(out *KameletList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Kamelet, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletList. +func (in *KameletList) DeepCopy() *KameletList { + if in == nil { + return nil + } + out := new(KameletList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KameletList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletSpec) DeepCopyInto(out *KameletSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletSpec. +func (in *KameletSpec) DeepCopy() *KameletSpec { + if in == nil { + return nil + } + out := new(KameletSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KameletStatus) DeepCopyInto(out *KameletStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KameletStatus. +func (in *KameletStatus) DeepCopy() *KameletStatus { + if in == nil { + return nil + } + out := new(KameletStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/controller/kamelet/action.go b/pkg/controller/kamelet/action.go new file mode 100644 index 0000000..7f07717 --- /dev/null +++ b/pkg/controller/kamelet/action.go @@ -0,0 +1,54 @@ +/* +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 kamelet + +import ( + "context" + + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "github.com/apache/camel-k/pkg/client" + "github.com/apache/camel-k/pkg/util/log" +) + +// Action -- +type Action interface { + client.Injectable + log.Injectable + + // a user friendly name for the action + Name() string + + // returns true if the action can handle the kamelet + CanHandle(kamelet *v1alpha1.Kamelet) bool + + // executes the handling function + Handle(ctx context.Context, kamelet *v1alpha1.Kamelet) (*v1alpha1.Kamelet, error) +} + +type baseAction struct { + client client.Client + L log.Logger +} + +func (action *baseAction) InjectClient(client client.Client) { + action.client = client +} + +func (action *baseAction) InjectLogger(log log.Logger) { + action.L = log +} diff --git a/pkg/controller/kamelet/initialize.go b/pkg/controller/kamelet/initialize.go new file mode 100644 index 0000000..e1f4360 --- /dev/null +++ b/pkg/controller/kamelet/initialize.go @@ -0,0 +1,46 @@ +/* +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 kamelet + +import ( + "context" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" +) + +// NewInitializeAction returns a action that initializes the kamelet configuration when not provided by the user +func NewInitializeAction() Action { + return &initializeAction{} +} + +type initializeAction struct { + baseAction +} + +func (action *initializeAction) Name() string { + return "initialize" +} + +func (action *initializeAction) CanHandle(kamelet *v1alpha1.Kamelet) bool { + return kamelet.Status.Phase == v1alpha1.KameletPhaseNone +} + +func (action *initializeAction) Handle(ctx context.Context, kamelet *v1alpha1.Kamelet) (*v1alpha1.Kamelet, error) { + target := kamelet.DeepCopy() + target.Status.Phase = v1alpha1.KameletPhaseReady + return target, nil +} diff --git a/pkg/controller/kamelet/kamelet_controller.go b/pkg/controller/kamelet/kamelet_controller.go new file mode 100644 index 0000000..048cf22 --- /dev/null +++ b/pkg/controller/kamelet/kamelet_controller.go @@ -0,0 +1,187 @@ +/* +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 kamelet + +import ( + "context" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" + "time" + + camelevent "github.com/apache/camel-k/pkg/event" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + + k8sclient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/apache/camel-k/pkg/client" +) + +// Add creates a new Kamelet Controller and adds it to the Manager. The Manager will set fields on the Controller +// and Start it when the Manager is Started. +func Add(mgr manager.Manager) error { + c, err := client.FromManager(mgr) + if err != nil { + return err + } + return add(mgr, newReconciler(mgr, c)) +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager, c client.Client) reconcile.Reconciler { + return &ReconcileKamelet{ + client: c, + scheme: mgr.GetScheme(), + recorder: mgr.GetEventRecorderFor("camel-k-kamelet-controller"), + } +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("kamelet-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for changes to primary resource Kamelet + err = c.Watch(&source.Kind{Type: &v1alpha1.Kamelet{}}, &handler.EnqueueRequestForObject{}, predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + oldKamelet := e.ObjectOld.(*v1alpha1.Kamelet) + newKamelet := e.ObjectNew.(*v1alpha1.Kamelet) + // Ignore updates to the kamelet status in which case metadata.Generation + // does not change, or except when the kamelet phase changes as it's used + // to transition from one phase to another + return oldKamelet.Generation != newKamelet.Generation || + oldKamelet.Status.Phase != newKamelet.Status.Phase + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Evaluates to false if the object has been confirmed deleted + return !e.DeleteStateUnknown + }, + }) + if err != nil { + return err + } + + return nil +} + +var _ reconcile.Reconciler = &ReconcileKamelet{} + +// ReconcileKamelet reconciles a Kamelet object +type ReconcileKamelet struct { + // This client, initialized using mgr.Client() above, is a split client + // that reads objects from the cache and writes to the apiserver + client client.Client + scheme *runtime.Scheme + recorder record.EventRecorder +} + +// Reconcile reads that state of the cluster for a Kamelet object and makes changes based +// on the state read and what is in the Kamelet.Spec +// Note: +// The Controller will requeue the Request to be processed again if the returned error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (r *ReconcileKamelet) Reconcile(request reconcile.Request) (reconcile.Result, error) { + rlog := Log.WithValues("request-namespace", request.Namespace, "request-name", request.Name) + rlog.Info("Reconciling Kamelet") + + ctx := context.TODO() + + // Fetch the Kamelet instance + var instance v1alpha1.Kamelet + + if err := r.client.Get(ctx, request.NamespacedName, &instance); err != nil { + if errors.IsNotFound(err) { + // Request object not found, could have been deleted after reconcile request. + // Owned objects are automatically garbage collected. For additional cleanup + // logic use finalizers. + + // Return and don't requeue + return reconcile.Result{}, nil + } + // Error reading the object - requeue the request. + return reconcile.Result{}, err + } + + actions := []Action{ + NewInitializeAction(), + } + + var targetPhase v1alpha1.KameletPhase + var err error + + target := instance.DeepCopy() + targetLog := rlog.ForKamelet(target) + + for _, a := range actions { + a.InjectClient(r.client) + a.InjectLogger(targetLog) + + if a.CanHandle(target) { + targetLog.Infof("Invoking action %s", a.Name()) + + phaseFrom := target.Status.Phase + + target, err = a.Handle(ctx, target) + if err != nil { + camelevent.NotifyKameletError(ctx, r.client, r.recorder, &instance, target, err) + return reconcile.Result{}, err + } + + if target != nil { + if err := r.client.Status().Patch(ctx, target, k8sclient.MergeFrom(&instance)); err != nil { + camelevent.NotifyKameletError(ctx, r.client, r.recorder, &instance, target, err) + return reconcile.Result{}, err + } + + targetPhase = target.Status.Phase + + if targetPhase != phaseFrom { + targetLog.Info( + "state transition", + "phase-from", phaseFrom, + "phase-to", target.Status.Phase, + ) + } + } + + // handle one action at time so the resource + // is always at its latest state + camelevent.NotifyKameletUpdated(ctx, r.client, r.recorder, &instance, target) + break + } + } + + if targetPhase == v1alpha1.KameletPhaseReady { + return reconcile.Result{}, nil + } + + // Requeue + return reconcile.Result{ + RequeueAfter: 5 * time.Second, + }, nil +} diff --git a/pkg/controller/kamelet/log.go b/pkg/controller/kamelet/log.go new file mode 100644 index 0000000..2df12a4 --- /dev/null +++ b/pkg/controller/kamelet/log.go @@ -0,0 +1,23 @@ +/* +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 kamelet + +import "github.com/apache/camel-k/pkg/util/log" + +// Log -- +var Log = log.Log.WithName("controller").WithName("kamelet") diff --git a/pkg/event/manager.go b/pkg/event/manager.go index 43d1051..93e27f9 100644 --- a/pkg/event/manager.go +++ b/pkg/event/manager.go @@ -20,6 +20,7 @@ package event import ( "context" "fmt" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/apache/camel-k/pkg/client" @@ -60,6 +61,13 @@ const ( // ReasonBuildError -- ReasonBuildError = "BuildError" + // ReasonKameletError -- + ReasonKameletError = "KameletError" + // ReasonKameletConditionChanged -- + ReasonKameletConditionChanged = "KameletConditionChanged" + // ReasonKameletPhaseUpdated -- + ReasonKameletPhaseUpdated = "KameletPhaseUpdated" + // ReasonRelatedObjectChanged -- ReasonRelatedObjectChanged = "ReasonRelatedObjectChanged" ) @@ -151,6 +159,35 @@ func NotifyIntegrationPlatformError(ctx context.Context, c client.Client, record recorder.Eventf(p, corev1.EventTypeWarning, ReasonIntegrationPlatformError, "Cannot reconcile Integration Platform %s: %v", p.Name, err) } +// NotifyKameletUpdated automatically generates events when a Kamelet changes +func NotifyKameletUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1alpha1.Kamelet) { + if new == nil { + return + } + oldPhase := "" + var oldConditions []v1.ResourceCondition + if old != nil { + oldPhase = string(old.Status.Phase) + oldConditions = old.Status.GetConditions() + } + if new.Status.Phase != v1alpha1.KameletPhaseNone { + notifyIfConditionUpdated(recorder, new, oldConditions, new.Status.GetConditions(), "Kamelet", new.Name, ReasonKameletConditionChanged) + } + notifyIfPhaseUpdated(ctx, c, recorder, new, oldPhase, string(new.Status.Phase), "Kamelet", new.Name, ReasonKameletPhaseUpdated, "") +} + +// NotifyKameletError automatically generates error events when the kamelet reconcile cycle phase has an error +func NotifyKameletError(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1alpha1.Kamelet, err error) { + k := old + if new != nil { + k = new + } + if k == nil { + return + } + recorder.Eventf(k, corev1.EventTypeWarning, ReasonKameletError, "Cannot reconcile Kamelet %s: %v", k.Name, err) +} + // NotifyBuildUpdated automatically generates events when a build changes func NotifyBuildUpdated(ctx context.Context, c client.Client, recorder record.EventRecorder, old, new *v1.Build) { if new == nil { diff --git a/pkg/install/cluster.go b/pkg/install/cluster.go index 9b67062..ae55328 100644 --- a/pkg/install/cluster.go +++ b/pkg/install/cluster.go @@ -20,6 +20,7 @@ package install import ( "context" "errors" + "fmt" "strconv" "time" @@ -45,27 +46,32 @@ func SetupClusterWideResourcesOrCollect(ctx context.Context, clientProvider clie } // Install CRD for Integration Platform (if needed) - if err := installCRD(ctx, c, "IntegrationPlatform", "crd-integration-platform.yaml", collection); err != nil { + if err := installCRD(ctx, c, "IntegrationPlatform", "v1", "crd-integration-platform.yaml", collection); err != nil { return err } // Install CRD for Integration Kit (if needed) - if err := installCRD(ctx, c, "IntegrationKit", "crd-integration-kit.yaml", collection); err != nil { + if err := installCRD(ctx, c, "IntegrationKit", "v1", "crd-integration-kit.yaml", collection); err != nil { return err } // Install CRD for Integration (if needed) - if err := installCRD(ctx, c, "Integration", "crd-integration.yaml", collection); err != nil { + if err := installCRD(ctx, c, "Integration", "v1", "crd-integration.yaml", collection); err != nil { return err } // Install CRD for Camel Catalog (if needed) - if err := installCRD(ctx, c, "CamelCatalog", "crd-camel-catalog.yaml", collection); err != nil { + if err := installCRD(ctx, c, "CamelCatalog", "v1", "crd-camel-catalog.yaml", collection); err != nil { return err } // Install CRD for Build (if needed) - if err := installCRD(ctx, c, "Build", "crd-build.yaml", collection); err != nil { + if err := installCRD(ctx, c, "Build", "v1", "crd-build.yaml", collection); err != nil { + return err + } + + // Install CRD for Kamelet (if needed) + if err := installCRD(ctx, c, "Kamelet", "v1alpha1", "crd-kamelet.yaml", collection); err != nil { return err } @@ -120,32 +126,37 @@ func WaitForAllCRDInstallation(ctx context.Context, clientProvider client.Provid // AreAllCRDInstalled check if all the required CRDs are installed func AreAllCRDInstalled(ctx context.Context, c client.Client) (bool, error) { - if ok, err := IsCRDInstalled(ctx, c, "IntegrationPlatform"); err != nil { + if ok, err := IsCRDInstalled(ctx, c, "IntegrationPlatform", "v1"); err != nil { + return ok, err + } else if !ok { + return false, nil + } + if ok, err := IsCRDInstalled(ctx, c, "IntegrationKit", "v1"); err != nil { return ok, err } else if !ok { return false, nil } - if ok, err := IsCRDInstalled(ctx, c, "IntegrationKit"); err != nil { + if ok, err := IsCRDInstalled(ctx, c, "Integration", "v1"); err != nil { return ok, err } else if !ok { return false, nil } - if ok, err := IsCRDInstalled(ctx, c, "Integration"); err != nil { + if ok, err := IsCRDInstalled(ctx, c, "CamelCatalog", "v1"); err != nil { return ok, err } else if !ok { return false, nil } - if ok, err := IsCRDInstalled(ctx, c, "CamelCatalog"); err != nil { + if ok, err := IsCRDInstalled(ctx, c, "Build", "v1"); err != nil { return ok, err } else if !ok { return false, nil } - return IsCRDInstalled(ctx, c, "Build") + return IsCRDInstalled(ctx, c, "Kamelet", "v1alpha1") } // IsCRDInstalled check if the given CRD kind is installed -func IsCRDInstalled(ctx context.Context, c client.Client, kind string) (bool, error) { - lst, err := c.Discovery().ServerResourcesForGroupVersion("camel.apache.org/v1") +func IsCRDInstalled(ctx context.Context, c client.Client, kind string, version string) (bool, error) { + lst, err := c.Discovery().ServerResourcesForGroupVersion(fmt.Sprintf("camel.apache.org/%s", version)) if err != nil && k8serrors.IsNotFound(err) { return false, nil } else if err != nil { @@ -159,7 +170,7 @@ func IsCRDInstalled(ctx context.Context, c client.Client, kind string) (bool, er return false, nil } -func installCRD(ctx context.Context, c client.Client, kind string, resourceName string, collection *kubernetes.Collection) error { +func installCRD(ctx context.Context, c client.Client, kind string, version string, resourceName string, collection *kubernetes.Collection) error { crd := deploy.Resource(resourceName) if collection != nil { unstr, err := kubernetes.LoadRawResourceFromYaml(string(crd)) @@ -171,7 +182,7 @@ func installCRD(ctx context.Context, c client.Client, kind string, resourceName } // Installing Integration CRD - installed, err := IsCRDInstalled(ctx, c, kind) + installed, err := IsCRDInstalled(ctx, c, kind, version) if err != nil { return err } diff --git a/pkg/util/log/log.go b/pkg/util/log/log.go index 8287561..7ac3573 100644 --- a/pkg/util/log/log.go +++ b/pkg/util/log/log.go @@ -19,6 +19,7 @@ package log import ( "fmt" + "github.com/apache/camel-k/pkg/apis/camel/v1alpha1" v1 "github.com/apache/camel-k/pkg/apis/camel/v1" "github.com/go-logr/logr" @@ -128,6 +129,16 @@ func (l Logger) ForIntegrationPlatform(target *v1.IntegrationPlatform) Logger { ) } +// ForIntegrationPlatform -- +func (l Logger) ForKamelet(target *v1alpha1.Kamelet) Logger { + return l.WithValues( + "api-version", target.APIVersion, + "kind", target.Kind, + "ns", target.Namespace, + "name", target.Name, + ) +} + // *********************************** // // Helpers
