diff --git a/Makefile b/Makefile index b9b630163..2303a36f4 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,7 @@ run: manifests generate ## Run a controller from your host. .PHONY: debug debug: build-4-debug ## Run a controller from your host from binary - ./bin/manager + ./bin/manager -v=2 .PHONY: docker-build docker-build: test ## Build docker image with the manager. diff --git a/api/condition_types.go b/api/condition_types.go index 6d948e8f9..3f13f15a5 100644 --- a/api/condition_types.go +++ b/api/condition_types.go @@ -15,6 +15,8 @@ package api import ( + "fmt" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -102,3 +104,17 @@ func (c *Condition) GetMessage() string { } return c.Message } + +func (c *Condition) String() string { + if c == nil { + return "" + } + str := fmt.Sprintf("Condition %s status is %s", c.Type, c.Status) + if len(c.Reason) > 0 { + str += fmt.Sprintf(". [Reason] %s", c.Reason) + } + if len(c.Message) > 0 { + str += fmt.Sprintf(". [Message] %s", c.Message) + } + return str +} diff --git a/api/status_types.go b/api/status_types.go index 765e90492..dde98dee0 100644 --- a/api/status_types.go +++ b/api/status_types.go @@ -54,6 +54,14 @@ func (s *Status) GetCondition(t ConditionType) *Condition { return nil } +func (s *Status) String() string { + str := "" + for _, c := range s.Conditions { + str += c.String() + "\n" + } + return str +} + func (s *Status) setConditions(c Conditions) { s.Conditions = c } diff --git a/api/v1alpha08/sonataflowplatform_build_types.go b/api/v1alpha08/sonataflowplatform_build_types.go new file mode 100644 index 000000000..94652810f --- /dev/null +++ b/api/v1alpha08/sonataflowplatform_build_types.go @@ -0,0 +1,102 @@ +// Copyright 2023 Red Hat, Inc. and/or its affiliates +// +// Licensed 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 v1alpha08 + +import ( + "strconv" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Describes the general build specification for this platform. Specific for build scenarios. +type BuildPlatformSpec struct { + // Describes a build template for building workflows. Base for the internal SonataFlowBuild resource. + Template BuildTemplate `json:"template,omitempty"` + // Describes the platform configuration for building workflows. + Config BuildPlatformConfig `json:"config,omitempty"` +} + +// Describes the configuration for building in the given platform +type BuildPlatformConfig struct { + // a base image that can be used as base layer for all images. + // It can be useful if you want to provide some custom base image with further utility software + BaseImage string `json:"baseImage,omitempty"` + // how much time to wait before time out the build process + Timeout *metav1.Duration `json:"timeout,omitempty"` + // BuildStrategy to use to build workflows in the platform. + // Usually, the operator elect the strategy based on the platform. + // Note that this field might be read only in certain scenarios. + BuildStrategy BuildStrategy `json:"strategy,omitempty"` + // BuildStrategyOptions additional options to add to the build strategy. + // See https://sonataflow.org/serverlessworkflow/main/cloud/operator/build-and-deploy-workflows.html + BuildStrategyOptions map[string]string `json:"strategyOptions,omitempty"` + // Registry the registry where to publish the built image + Registry RegistrySpec `json:"registry,omitempty"` +} + +// GetTimeout returns the specified duration or a default one +func (b *BuildPlatformConfig) GetTimeout() metav1.Duration { + if b.Timeout == nil { + return metav1.Duration{} + } + return *b.Timeout +} + +// IsStrategyOptionEnabled return whether the BuildStrategyOptions is enabled or not +func (b *BuildPlatformConfig) IsStrategyOptionEnabled(option string) bool { + if enabled, ok := b.BuildStrategyOptions[option]; ok { + res, err := strconv.ParseBool(enabled) + if err != nil { + return false + } + return res + } + return false +} + +func (b *BuildPlatformConfig) IsStrategyOptionEmpty(option string) bool { + if v, ok := b.BuildStrategyOptions[option]; ok { + return len(v) == 0 + } + return false +} + +// RegistrySpec provides the configuration for the container registry +type RegistrySpec struct { + // if the container registry is insecure (ie, http only) + Insecure bool `json:"insecure,omitempty"` + // the URI to access + Address string `json:"address,omitempty"` + // the secret where credentials are stored + Secret string `json:"secret,omitempty"` + // the configmap which stores the Certificate Authority + CA string `json:"ca,omitempty"` + // the registry organization + Organization string `json:"organization,omitempty"` +} + +type BuildStrategy string + +const ( + // OperatorBuildStrategy uses the operator builder to perform the workflow build + // E.g. on Minikube or Kubernetes the container-builder strategies + OperatorBuildStrategy BuildStrategy = "operator" + // PlatformBuildStrategy uses the cluster to perform the build. + // E.g. on OpenShift, BuildConfig. + PlatformBuildStrategy BuildStrategy = "platform" + + // In the future we can have "custom" which will delegate the build to an external actor provided by the administrator + // See https://issues.redhat.com/browse/KOGITO-9084 +) diff --git a/api/v1alpha08/sonataflowplatform_devmode_types.go b/api/v1alpha08/sonataflowplatform_devmode_types.go new file mode 100644 index 000000000..971fbb356 --- /dev/null +++ b/api/v1alpha08/sonataflowplatform_devmode_types.go @@ -0,0 +1,21 @@ +// Copyright 2023 Red Hat, Inc. and/or its affiliates +// +// Licensed 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 v1alpha08 + +// DevModePlatformSpec describes the devmode configuration for the given platform. +type DevModePlatformSpec struct { + // Base image to run the Workflow in dev mode instead of the operator's default. + BaseImage string `json:"baseImage,omitempty"` +} diff --git a/api/v1alpha08/sonataflowplatform_types.go b/api/v1alpha08/sonataflowplatform_types.go index a43a94fff..c79172a8e 100644 --- a/api/v1alpha08/sonataflowplatform_types.go +++ b/api/v1alpha08/sonataflowplatform_types.go @@ -15,37 +15,24 @@ package v1alpha08 import ( - "strconv" - - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// ConfigurationSpecType is used to define the enum values of the supported types for ConfigurationSpec -type ConfigurationSpecType string -const ( - // PropertyConfigurationSpec ... - PropertyConfigurationSpec ConfigurationSpecType = "property" - // ConfigMapConfigurationSpec ... - ConfigMapConfigurationSpec ConfigurationSpecType = "configmap" - // SecretConfigurationSpec ... - SecretConfigurationSpec ConfigurationSpecType = "secret" + "github.com/kiegroup/kogito-serverless-operator/api" ) -// ConfigurationSpec represents a generic configuration specification -type ConfigurationSpec struct { - // Type represents the type of configuration, ie: property, configmap, secret, ... - Type ConfigurationSpecType `json:"type"` - // Value a reference to the object for this configuration (syntax may vary depending on the `Type`) - Value corev1.ObjectReference `json:"value"` -} - const ( // SonataFlowPlatformKind is the Kind name of the SonataFlowPlatform CR SonataFlowPlatformKind string = "SonataFlowPlatform" ) +// SonataFlowPlatformSpec defines the desired state of SonataFlowPlatform +type SonataFlowPlatformSpec struct { + // Attributes for building workflows in the target platform + Build BuildPlatformSpec `json:"build,omitempty"` + // Attributes for running workflows in devmode (immutable, no build required) + DevMode DevModePlatformSpec `json:"devMode,omitempty"` +} + // PlatformCluster is the kind of orchestration cluster the platform is installed into // +kubebuilder:validation:Enum=kubernetes;openshift type PlatformCluster string @@ -57,146 +44,58 @@ const ( PlatformClusterKubernetes PlatformCluster = "kubernetes" ) -// RegistrySpec provides the configuration for the container registry -type RegistrySpec struct { - // if the container registry is insecure (ie, http only) - Insecure bool `json:"insecure,omitempty"` - // the URI to access - Address string `json:"address,omitempty"` - // the secret where credentials are stored - Secret string `json:"secret,omitempty"` - // the configmap which stores the Certificate Authority - CA string `json:"ca,omitempty"` - // the registry organization - Organization string `json:"organization,omitempty"` -} - -type BuildStrategy string - const ( - // OperatorBuildStrategy uses the operator builder to perform the workflow build - // E.g. on Minikube or Kubernetes the container-builder strategies - OperatorBuildStrategy BuildStrategy = "operator" - // PlatformBuildStrategy uses the cluster to perform the build. - // E.g. on OpenShift, BuildConfig. - PlatformBuildStrategy BuildStrategy = "platform" - - // In the future we can have "custom" which will delegate the build to an external actor provided by the administrator - // See https://issues.redhat.com/browse/KOGITO-9084 + PlatformCreatingReason = "Creating" + PlatformWarmingReason = "Warming" + PlatformFailureReason = "Failure" + PlatformDuplicatedReason = "Duplicated" ) -type BuildPlatformTemplate struct { - // a base image that can be used as base layer for all images. - // It can be useful if you want to provide some custom base image with further utility software - BaseImage string `json:"baseImage,omitempty"` - // how much time to wait before time out the build process - Timeout *metav1.Duration `json:"timeout,omitempty"` - // BuildStrategy to use to build workflows in the platform. - // Usually, the operator elect the strategy based on the platform. - // Note that this field might be read only in certain scenarios. - BuildStrategy BuildStrategy `json:"buildStrategy,omitempty"` - // TODO: add a link to the documentation where the user can find more info about this field - // BuildStrategyOptions additional options to add to the build strategy. - BuildStrategyOptions map[string]string `json:"buildStrategyOptions,omitempty"` - // Registry the registry where to publish the built image - Registry RegistrySpec `json:"registry,omitempty"` +// SonataFlowPlatformStatus defines the observed state of SonataFlowPlatform +type SonataFlowPlatformStatus struct { + api.Status `json:",inline"` + // Cluster what kind of cluster you're running (ie, plain Kubernetes or OpenShift) + Cluster PlatformCluster `json:"cluster,omitempty"` + // Version the operator version controlling this Platform + Version string `json:"version,omitempty"` + // Info generic information related to the build + Info map[string]string `json:"info,omitempty"` } -// GetTimeout returns the specified duration or a default one -func (b *BuildPlatformTemplate) GetTimeout() metav1.Duration { - if b.Timeout == nil { - return metav1.Duration{} - } - return *b.Timeout +func (in *SonataFlowPlatformStatus) GetTopLevelConditionType() api.ConditionType { + return api.SucceedConditionType } -// IsOptionEnabled return whether the BuildStrategyOptions is enabled or not -func (b *BuildPlatformTemplate) IsOptionEnabled(option string) bool { - if enabled, ok := b.BuildStrategyOptions[option]; ok { - res, err := strconv.ParseBool(enabled) - if err != nil { - return false - } - return res - } - return false +func (in *SonataFlowPlatformStatus) IsReady() bool { + return in.GetTopLevelCondition().IsTrue() } -func (b *BuildPlatformTemplate) IsOptionEmpty(option string) bool { - if v, ok := b.BuildStrategyOptions[option]; ok { - return len(v) == 0 - } - return false +func (in *SonataFlowPlatformStatus) GetTopLevelCondition() *api.Condition { + return in.GetCondition(in.GetTopLevelConditionType()) } -// SonataFlowPlatformSpec defines the desired state of SonataFlowPlatform -type SonataFlowPlatformSpec struct { - // BuildTemplate specify how to build the Workflow. It's used as a template for the SonataFlowBuild - BuildTemplate BuildTemplate `json:"build,omitempty"` - // BuildPlatform specify how is the platform where we want to build the Workflow - BuildPlatform BuildPlatformTemplate `json:"platform,omitempty"` - // Configuration list of configuration properties to be attached to all the Workflow built from this Platform - Configuration ConfigurationSpec `json:"configuration,omitempty"` - // DevBaseImage Base image to run the Workflow in dev mode instead of the operator's default. - // Optional, used for the dev profile only - DevBaseImage string `json:"devBaseImage,omitempty"` +func (in *SonataFlowPlatformStatus) Manager() api.ConditionsManager { + return api.NewConditionManager(in, api.SucceedConditionType) } -// PlatformPhase is the phase of a Platform -type PlatformPhase string +func (in *SonataFlowPlatformStatus) IsCreating() bool { + cond := in.GetTopLevelCondition() + return cond.IsFalse() && cond.Reason == PlatformCreatingReason +} -const ( - // PlatformPhaseNone when the SonataFlowPlatform does not exist - PlatformPhaseNone PlatformPhase = "" - // PlatformPhaseCreating when the SonataFlowPlatform is under creation process - PlatformPhaseCreating PlatformPhase = "Creating" - // PlatformPhaseWarming when the SonataFlowPlatform is warming (ie, creating Kaniko cache) - PlatformPhaseWarming PlatformPhase = "Warming" - // PlatformPhaseReady when the SonataFlowPlatform is ready - PlatformPhaseReady PlatformPhase = "Ready" - // PlatformPhaseError when the SonataFlowPlatform had some error (see Conditions) - PlatformPhaseError PlatformPhase = "Error" - // PlatformPhaseDuplicate when the SonataFlowPlatform is duplicated - PlatformPhaseDuplicate PlatformPhase = "Duplicate" -) +func (in *SonataFlowPlatformStatus) IsWarming() bool { + cond := in.GetTopLevelCondition() + return cond.IsFalse() && cond.Reason == PlatformWarmingReason +} -// PlatformConditionType defines the type of condition -type PlatformConditionType string - -// PlatformCondition describes the state of a resource at a certain point. -type PlatformCondition struct { - // TODO: the Type can't be Kubernetes or OpenShift, but the actual condition like "Ready". See the Conditions implementation in the workflow. - // TODO: also, we already have the `Cluster` field for that matter. - // TODO: see https://issues.redhat.com/browse/KOGITO-9218 - - // Type of platform condition (i.e. Kubernetes, OpenShift). - Type PlatformConditionType `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"` +func (in *SonataFlowPlatformStatus) IsDuplicated() bool { + cond := in.GetTopLevelCondition() + return cond.IsFalse() && cond.Reason == PlatformDuplicatedReason } -// SonataFlowPlatformStatus defines the observed state of SonataFlowPlatform -type SonataFlowPlatformStatus struct { - // Cluster what kind of cluster you're running (ie, plain Kubernetes or OpenShift) - Cluster PlatformCluster `json:"cluster,omitempty"` - // ObservedGeneration is the most recent generation observed for this Platform. - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Phase defines in what phase the Platform is found - Phase PlatformPhase `json:"phase,omitempty"` - // Conditions which are the conditions met (particularly useful when in ERROR phase) - Conditions []PlatformCondition `json:"conditions,omitempty"` - // Version the operator version controlling this Platform - Version string `json:"version,omitempty"` - // Info generic information related to the build - Info map[string]string `json:"info,omitempty"` +func (in *SonataFlowPlatformStatus) IsFailure() bool { + cond := in.GetTopLevelCondition() + return cond.IsFalse() && cond.Reason == PlatformFailureReason } // SonataFlowPlatform is the descriptor for the workflow platform infrastructure. @@ -206,8 +105,8 @@ type SonataFlowPlatformStatus struct { // +kubebuilder:subresource:status // +kubebuilder:resource:shortName={"sfp", "sfplatform", "sfplatforms"} // +kubebuilder:printcolumn:name="Cluster",type=string,JSONPath=`.status.cluster` -// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` -// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.phase=='Ready'` +// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=='Succeed')].status` +// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=='Succeed')].reason` type SonataFlowPlatform struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` diff --git a/api/v1alpha08/sonataflowplatform_types_support.go b/api/v1alpha08/sonataflowplatform_types_support.go index d89528637..c6adac741 100644 --- a/api/v1alpha08/sonataflowplatform_types_support.go +++ b/api/v1alpha08/sonataflowplatform_types_support.go @@ -15,7 +15,6 @@ package v1alpha08 import ( - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -28,137 +27,3 @@ func NewSonataFlowPlatformList() SonataFlowPlatformList { }, } } - -// NewSonataFlowPlatform returns the basic Platform definition -func NewSonataFlowPlatform(namespace string, name string) SonataFlowPlatform { - return SonataFlowPlatform{ - TypeMeta: metav1.TypeMeta{ - APIVersion: GroupVersion.String(), - Kind: SonataFlowPlatformKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - } -} - -// GetCondition returns the condition with the provided type. -func (in *SonataFlowPlatformStatus) GetCondition(condType PlatformConditionType) *PlatformCondition { - for i := range in.Conditions { - c := in.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -// SetErrorCondition sets the condition error for the given platform -func (in *SonataFlowPlatformStatus) SetErrorCondition(condType PlatformConditionType, reason string, err error) { - in.SetConditions(PlatformCondition{ - Type: condType, - Status: corev1.ConditionFalse, - LastUpdateTime: metav1.Now(), - LastTransitionTime: metav1.Now(), - Reason: reason, - Message: err.Error(), - }) -} - -// SetConditions updates the resource to include the provided conditions. -// -// If a condition that we are about to add already exists and has the same status and -// reason then we are not going to update. -func (in *SonataFlowPlatformStatus) SetConditions(conditions ...PlatformCondition) { - for _, condition := range conditions { - if condition.LastUpdateTime.IsZero() { - condition.LastUpdateTime = metav1.Now() - } - if condition.LastTransitionTime.IsZero() { - condition.LastTransitionTime = metav1.Now() - } - - currentCond := in.GetCondition(condition.Type) - - if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { - return - } - // Do not update lastTransitionTime if the status of the condition doesn't change. - if currentCond != nil && currentCond.Status == condition.Status { - condition.LastTransitionTime = currentCond.LastTransitionTime - } - - in.RemoveCondition(condition.Type) - in.Conditions = append(in.Conditions, condition) - } -} - -// RemoveCondition removes the resource condition with the provided type. -func (in *SonataFlowPlatformStatus) RemoveCondition(condType PlatformConditionType) { - newConditions := in.Conditions[:0] - for _, c := range in.Conditions { - if c.Type != condType { - newConditions = append(newConditions, c) - } - } - - in.Conditions = newConditions -} - -const ( - // ServiceTypeUser service user type label marker - ServiceTypeUser = "user" -) - -// +kubebuilder:object:generate=false -// ResourceCondition is a common type for all conditions -type ResourceCondition interface { - GetType() string - GetStatus() corev1.ConditionStatus - GetLastUpdateTime() metav1.Time - GetLastTransitionTime() metav1.Time - GetReason() string - GetMessage() string -} - -var _ ResourceCondition = PlatformCondition{} - -// GetConditions -- -func (in *SonataFlowPlatformStatus) GetConditions() []ResourceCondition { - res := make([]ResourceCondition, 0, len(in.Conditions)) - for _, c := range in.Conditions { - res = append(res, c) - } - return res -} - -// GetType -- -func (c PlatformCondition) GetType() string { - return string(c.Type) -} - -// GetStatus -- -func (c PlatformCondition) GetStatus() corev1.ConditionStatus { - return c.Status -} - -// GetLastUpdateTime -- -func (c PlatformCondition) GetLastUpdateTime() metav1.Time { - return c.LastUpdateTime -} - -// GetLastTransitionTime -- -func (c PlatformCondition) GetLastTransitionTime() metav1.Time { - return c.LastTransitionTime -} - -// GetReason -- -func (c PlatformCondition) GetReason() string { - return c.Reason -} - -// GetMessage -- -func (c PlatformCondition) GetMessage() string { - return c.Message -} diff --git a/api/v1alpha08/zz_generated.deepcopy.go b/api/v1alpha08/zz_generated.deepcopy.go index d44f21520..99985b0e2 100644 --- a/api/v1alpha08/zz_generated.deepcopy.go +++ b/api/v1alpha08/zz_generated.deepcopy.go @@ -27,7 +27,7 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BuildPlatformTemplate) DeepCopyInto(out *BuildPlatformTemplate) { +func (in *BuildPlatformConfig) DeepCopyInto(out *BuildPlatformConfig) { *out = *in if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout @@ -44,12 +44,29 @@ func (in *BuildPlatformTemplate) DeepCopyInto(out *BuildPlatformTemplate) { out.Registry = in.Registry } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BuildPlatformTemplate. -func (in *BuildPlatformTemplate) DeepCopy() *BuildPlatformTemplate { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BuildPlatformConfig. +func (in *BuildPlatformConfig) DeepCopy() *BuildPlatformConfig { if in == nil { return nil } - out := new(BuildPlatformTemplate) + out := new(BuildPlatformConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BuildPlatformSpec) DeepCopyInto(out *BuildPlatformSpec) { + *out = *in + in.Template.DeepCopyInto(&out.Template) + in.Config.DeepCopyInto(&out.Config) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BuildPlatformSpec. +func (in *BuildPlatformSpec) DeepCopy() *BuildPlatformSpec { + if in == nil { + return nil + } + out := new(BuildPlatformSpec) in.DeepCopyInto(out) return out } @@ -93,17 +110,16 @@ func (in *ConfigMapWorkflowResource) DeepCopy() *ConfigMapWorkflowResource { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigurationSpec) DeepCopyInto(out *ConfigurationSpec) { +func (in *DevModePlatformSpec) DeepCopyInto(out *DevModePlatformSpec) { *out = *in - out.Value = in.Value } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigurationSpec. -func (in *ConfigurationSpec) DeepCopy() *ConfigurationSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DevModePlatformSpec. +func (in *DevModePlatformSpec) DeepCopy() *DevModePlatformSpec { if in == nil { return nil } - out := new(ConfigurationSpec) + out := new(DevModePlatformSpec) in.DeepCopyInto(out) return out } @@ -200,23 +216,6 @@ func (in *Flow) DeepCopy() *Flow { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PlatformCondition) DeepCopyInto(out *PlatformCondition) { - *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlatformCondition. -func (in *PlatformCondition) DeepCopy() *PlatformCondition { - if in == nil { - return nil - } - out := new(PlatformCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistrySpec) DeepCopyInto(out *RegistrySpec) { *out = *in @@ -444,9 +443,8 @@ func (in *SonataFlowPlatformList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SonataFlowPlatformSpec) DeepCopyInto(out *SonataFlowPlatformSpec) { *out = *in - in.BuildTemplate.DeepCopyInto(&out.BuildTemplate) - in.BuildPlatform.DeepCopyInto(&out.BuildPlatform) - out.Configuration = in.Configuration + in.Build.DeepCopyInto(&out.Build) + out.DevMode = in.DevMode } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SonataFlowPlatformSpec. @@ -462,13 +460,7 @@ func (in *SonataFlowPlatformSpec) DeepCopy() *SonataFlowPlatformSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SonataFlowPlatformStatus) DeepCopyInto(out *SonataFlowPlatformStatus) { *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]PlatformCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } + in.Status.DeepCopyInto(&out.Status) if in.Info != nil { in, out := &in.Info, &out.Info *out = make(map[string]string, len(*in)) diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml index 5128b0959..2fc42f029 100644 --- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml +++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml @@ -83,10 +83,12 @@ metadata: "name": "sonataflow-platform" }, "spec": { - "platform": { - "registry": { - "address": "quay.io/kiegroup", - "secret": "regcred" + "build": { + "config": { + "registry": { + "address": "quay.io/kiegroup", + "secret": "regcred" + } } } } diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index a9ddbc7ae..5d417a84b 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -22,12 +22,12 @@ spec: - jsonPath: .status.cluster name: Cluster type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.phase=='Ready' + - jsonPath: .status.conditions[?(@.type=='Succeed')].status name: Ready type: string + - jsonPath: .status.conditions[?(@.type=='Succeed')].reason + name: Reason + type: string name: v1alpha08 schema: openAPIV3Schema: @@ -50,172 +50,132 @@ spec: description: SonataFlowPlatformSpec defines the desired state of SonataFlowPlatform properties: build: - description: BuildTemplate specify how to build the Workflow. It's - used as a template for the SonataFlowBuild + description: Attributes for building workflows in the target platform properties: - arguments: - description: Arguments lists the command line arguments to send - to the builder - items: - type: string - type: array - resources: - description: Resources optional compute resource requirements - for the builder + config: + description: Describes the platform configuration for building + workflows. properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + baseImage: + description: a base image that can be used as base layer for + all images. It can be useful if you want to provide some + custom base image with further utility software + type: string + registry: + description: Registry the registry where to publish the built + image + properties: + address: + description: the URI to access + type: string + ca: + description: the configmap which stores the Certificate + Authority + type: string + insecure: + description: if the container registry is insecure (ie, + http only) + type: boolean + organization: + description: the registry organization + type: string + secret: + description: the secret where credentials are stored + type: string type: object - requests: + strategy: + description: BuildStrategy to use to build workflows in the + platform. Usually, the operator elect the strategy based + on the platform. Note that this field might be read only + in certain scenarios. + type: string + strategyOptions: additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: string + description: BuildStrategyOptions additional options to add + to the build strategy. See https://sonataflow.org/serverlessworkflow/main/cloud/operator/build-and-deploy-workflows.html type: object + timeout: + description: how much time to wait before time out the build + process + type: string type: object - timeout: - description: Timeout defines the Build maximum execution duration. - The Build deadline is set to the Build start time plus the Timeout - duration. If the Build deadline is exceeded, the Build context - is canceled, and its phase set to BuildPhaseFailed. - format: duration - type: string - type: object - configuration: - description: Configuration list of configuration properties to be - attached to all the Workflow built from this Platform - properties: - type: - description: 'Type represents the type of configuration, ie: property, - configmap, secret, ...' - type: string - value: - description: Value a reference to the object for this configuration - (syntax may vary depending on the `Type`) + template: + description: Describes a build template for building workflows. + Base for the internal SonataFlowBuild resource. properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead - of an entire object, this string should contain a valid - JSON/Go field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part - of an object. TODO: this design is not final and this field - is subject to change in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + arguments: + description: Arguments lists the command line arguments to + send to the builder + items: + type: string + type: array + resources: + description: Resources optional compute resource requirements + for the builder + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. \n This field + is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + timeout: + description: Timeout defines the Build maximum execution duration. + The Build deadline is set to the Build start time plus the + Timeout duration. If the Build deadline is exceeded, the + Build context is canceled, and its phase set to BuildPhaseFailed. + format: duration type: string type: object - x-kubernetes-map-type: atomic - required: - - type - - value type: object - devBaseImage: - description: DevBaseImage Base image to run the Workflow in dev mode - instead of the operator's default. Optional, used for the dev profile - only - type: string - platform: - description: BuildPlatform specify how is the platform where we want - to build the Workflow + devMode: + description: Attributes for running workflows in devmode (immutable, + no build required) properties: baseImage: - description: a base image that can be used as base layer for all - images. It can be useful if you want to provide some custom - base image with further utility software - type: string - buildStrategy: - description: BuildStrategy to use to build workflows in the platform. - Usually, the operator elect the strategy based on the platform. - Note that this field might be read only in certain scenarios. - type: string - buildStrategyOptions: - additionalProperties: - type: string - description: 'TODO: add a link to the documentation where the - user can find more info about this field BuildStrategyOptions - additional options to add to the build strategy.' - type: object - registry: - description: Registry the registry where to publish the built - image - properties: - address: - description: the URI to access - type: string - ca: - description: the configmap which stores the Certificate Authority - type: string - insecure: - description: if the container registry is insecure (ie, http - only) - type: boolean - organization: - description: the registry organization - type: string - secret: - description: the secret where credentials are stored - type: string - type: object - timeout: - description: how much time to wait before time out the build process + description: Base image to run the Workflow in dev mode instead + of the operator's default. type: string type: object type: object @@ -230,17 +190,12 @@ spec: - openshift type: string conditions: - description: Conditions which are the conditions met (particularly - useful when in ERROR phase) + description: The latest available observations of a resource's current + state. items: - description: PlatformCondition describes the state of a resource - at a certain point. + description: Condition describes the common structure for conditions + in our types properties: - lastTransitionTime: - description: Last time the condition transitioned from one status - to another. - format: date-time - type: string lastUpdateTime: description: The last time this condition was updated. format: date-time @@ -256,7 +211,7 @@ spec: description: Status of the condition, one of True, False, Unknown. type: string type: - description: Type of platform condition (i.e. Kubernetes, OpenShift). + description: Type condition for the given object type: string required: - status @@ -269,13 +224,9 @@ spec: description: Info generic information related to the build type: object observedGeneration: - description: ObservedGeneration is the most recent generation observed - for this Platform. + description: The generation observed by the deployment controller. format: int64 type: integer - phase: - description: Phase defines in what phase the Platform is found - type: string version: description: Version the operator version controlling this Platform type: string diff --git a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml index 805fed80a..a13e8727f 100644 --- a/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml +++ b/config/crd/bases/sonataflow.org_sonataflowplatforms.yaml @@ -23,12 +23,12 @@ spec: - jsonPath: .status.cluster name: Cluster type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.phase=='Ready' + - jsonPath: .status.conditions[?(@.type=='Succeed')].status name: Ready type: string + - jsonPath: .status.conditions[?(@.type=='Succeed')].reason + name: Reason + type: string name: v1alpha08 schema: openAPIV3Schema: @@ -51,172 +51,132 @@ spec: description: SonataFlowPlatformSpec defines the desired state of SonataFlowPlatform properties: build: - description: BuildTemplate specify how to build the Workflow. It's - used as a template for the SonataFlowBuild + description: Attributes for building workflows in the target platform properties: - arguments: - description: Arguments lists the command line arguments to send - to the builder - items: - type: string - type: array - resources: - description: Resources optional compute resource requirements - for the builder + config: + description: Describes the platform configuration for building + workflows. properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + baseImage: + description: a base image that can be used as base layer for + all images. It can be useful if you want to provide some + custom base image with further utility software + type: string + registry: + description: Registry the registry where to publish the built + image + properties: + address: + description: the URI to access + type: string + ca: + description: the configmap which stores the Certificate + Authority + type: string + insecure: + description: if the container registry is insecure (ie, + http only) + type: boolean + organization: + description: the registry organization + type: string + secret: + description: the secret where credentials are stored + type: string type: object - requests: + strategy: + description: BuildStrategy to use to build workflows in the + platform. Usually, the operator elect the strategy based + on the platform. Note that this field might be read only + in certain scenarios. + type: string + strategyOptions: additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: string + description: BuildStrategyOptions additional options to add + to the build strategy. See https://sonataflow.org/serverlessworkflow/main/cloud/operator/build-and-deploy-workflows.html type: object + timeout: + description: how much time to wait before time out the build + process + type: string type: object - timeout: - description: Timeout defines the Build maximum execution duration. - The Build deadline is set to the Build start time plus the Timeout - duration. If the Build deadline is exceeded, the Build context - is canceled, and its phase set to BuildPhaseFailed. - format: duration - type: string - type: object - configuration: - description: Configuration list of configuration properties to be - attached to all the Workflow built from this Platform - properties: - type: - description: 'Type represents the type of configuration, ie: property, - configmap, secret, ...' - type: string - value: - description: Value a reference to the object for this configuration - (syntax may vary depending on the `Type`) + template: + description: Describes a build template for building workflows. + Base for the internal SonataFlowBuild resource. properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead - of an entire object, this string should contain a valid - JSON/Go field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part - of an object. TODO: this design is not final and this field - is subject to change in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + arguments: + description: Arguments lists the command line arguments to + send to the builder + items: + type: string + type: array + resources: + description: Resources optional compute resource requirements + for the builder + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. \n This field + is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + timeout: + description: Timeout defines the Build maximum execution duration. + The Build deadline is set to the Build start time plus the + Timeout duration. If the Build deadline is exceeded, the + Build context is canceled, and its phase set to BuildPhaseFailed. + format: duration type: string type: object - x-kubernetes-map-type: atomic - required: - - type - - value type: object - devBaseImage: - description: DevBaseImage Base image to run the Workflow in dev mode - instead of the operator's default. Optional, used for the dev profile - only - type: string - platform: - description: BuildPlatform specify how is the platform where we want - to build the Workflow + devMode: + description: Attributes for running workflows in devmode (immutable, + no build required) properties: baseImage: - description: a base image that can be used as base layer for all - images. It can be useful if you want to provide some custom - base image with further utility software - type: string - buildStrategy: - description: BuildStrategy to use to build workflows in the platform. - Usually, the operator elect the strategy based on the platform. - Note that this field might be read only in certain scenarios. - type: string - buildStrategyOptions: - additionalProperties: - type: string - description: 'TODO: add a link to the documentation where the - user can find more info about this field BuildStrategyOptions - additional options to add to the build strategy.' - type: object - registry: - description: Registry the registry where to publish the built - image - properties: - address: - description: the URI to access - type: string - ca: - description: the configmap which stores the Certificate Authority - type: string - insecure: - description: if the container registry is insecure (ie, http - only) - type: boolean - organization: - description: the registry organization - type: string - secret: - description: the secret where credentials are stored - type: string - type: object - timeout: - description: how much time to wait before time out the build process + description: Base image to run the Workflow in dev mode instead + of the operator's default. type: string type: object type: object @@ -231,17 +191,12 @@ spec: - openshift type: string conditions: - description: Conditions which are the conditions met (particularly - useful when in ERROR phase) + description: The latest available observations of a resource's current + state. items: - description: PlatformCondition describes the state of a resource - at a certain point. + description: Condition describes the common structure for conditions + in our types properties: - lastTransitionTime: - description: Last time the condition transitioned from one status - to another. - format: date-time - type: string lastUpdateTime: description: The last time this condition was updated. format: date-time @@ -257,7 +212,7 @@ spec: description: Status of the condition, one of True, False, Unknown. type: string type: - description: Type of platform condition (i.e. Kubernetes, OpenShift). + description: Type condition for the given object type: string required: - status @@ -270,13 +225,9 @@ spec: description: Info generic information related to the build type: object observedGeneration: - description: ObservedGeneration is the most recent generation observed - for this Platform. + description: The generation observed by the deployment controller. format: int64 type: integer - phase: - description: Phase defines in what phase the Platform is found - type: string version: description: Version the operator version controlling this Platform type: string diff --git a/config/samples/sonataflow.org_v1alpha08_sonataflowplatform.yaml b/config/samples/sonataflow.org_v1alpha08_sonataflowplatform.yaml index 7b6069e42..cc5b3cbb9 100644 --- a/config/samples/sonataflow.org_v1alpha08_sonataflowplatform.yaml +++ b/config/samples/sonataflow.org_v1alpha08_sonataflowplatform.yaml @@ -3,7 +3,8 @@ kind: SonataFlowPlatform metadata: name: sonataflow-platform spec: - platform: - registry: - address: quay.io/kiegroup - secret: regcred + build: + config: + registry: + address: quay.io/kiegroup + secret: regcred diff --git a/container-builder/api/build_types.go b/container-builder/api/build_types.go index 37c3528f8..16230150b 100644 --- a/container-builder/api/build_types.go +++ b/container-builder/api/build_types.go @@ -17,6 +17,8 @@ package api import ( + "fmt" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -95,6 +97,15 @@ type PublishTask struct { Registry ContainerRegistrySpec `json:"registry,omitempty"` } +// GetRepositoryImageTag gets the full qualified Repository Name for the given image in the PublishTask. +// For example quay.io/myrepo/myimage:latest. +func (p *PublishTask) GetRepositoryImageTag() string { + if len(p.Registry.Address) > 0 { + return fmt.Sprintf("%s/%s", p.Registry.Address, p.Image) + } + return p.Image +} + // KanikoTask is used to configure Kaniko type KanikoTask struct { ContainerBuildBaseTask `json:",inline"` @@ -167,7 +178,7 @@ type ContainerBuildStatus struct { // describes the phase Phase ContainerBuildPhase `json:"phase,omitempty"` // the image name built - Image string `json:"image,omitempty"` + RepositoryImageTag string `json:"repositoryImageTag,omitempty"` // the digest from image Digest string `json:"digest,omitempty"` // the base image used for this build diff --git a/container-builder/builder/kubernetes/kaniko.go b/container-builder/builder/kubernetes/kaniko.go index 2041a9725..9299d0f95 100644 --- a/container-builder/builder/kubernetes/kaniko.go +++ b/container-builder/builder/kubernetes/kaniko.go @@ -77,7 +77,7 @@ func addKanikoTaskToPod(ctx context.Context, c client.Client, build *api.Contain args := []string{ "--dockerfile=Dockerfile", "--context=dir://" + task.ContextDir, - "--destination=" + task.Registry.Address + "/" + task.Image, + "--destination=" + task.GetRepositoryImageTag(), } if task.AdditionalFlags != nil && len(task.AdditionalFlags) > 0 { diff --git a/container-builder/builder/kubernetes/monitor_pod.go b/container-builder/builder/kubernetes/monitor_pod.go index bb8bf1183..abc1d82c7 100644 --- a/container-builder/builder/kubernetes/monitor_pod.go +++ b/container-builder/builder/kubernetes/monitor_pod.go @@ -121,7 +121,7 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *api.Container for _, task := range build.Spec.Tasks { if t := task.Kaniko; t != nil { - build.Status.Image = t.Image + build.Status.RepositoryImageTag = t.GetRepositoryImageTag() break } } diff --git a/controllers/builder/config.go b/controllers/builder/config.go index e64be2d27..3b98f5d46 100644 --- a/controllers/builder/config.go +++ b/controllers/builder/config.go @@ -19,15 +19,13 @@ import ( "fmt" "os" - "k8s.io/klog/v2" - "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" - operatorapi "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08" "github.com/kiegroup/kogito-serverless-operator/log" ) @@ -37,24 +35,8 @@ const ( ConfigMapName = "sonataflow-operator-builder-config" configKeyDefaultExtension = "DEFAULT_WORKFLOW_EXTENSION" configKeyDefaultBuilderResourceName = "DEFAULT_BUILDER_RESOURCE_NAME" - configKeyBuildNamespace = "build-namespace" - configKeyRegistrySecret = "registry-secret" - configKeyRegistryAddress = "registry-address" ) -func NewCustomConfig(platform operatorapi.SonataFlowPlatform) (map[string]string, error) { - customConfig := make(map[string]string) - if platform.Namespace == "" { - return nil, fmt.Errorf("unable to retrieve the namespace from platform %s", platform.Name) - } - customConfig[configKeyBuildNamespace] = platform.Namespace - // Registry Secret and Address are not required, the inner builder will use minikube inner registry if available - // TODO: we should review - customConfig[configKeyRegistrySecret] = platform.Spec.BuildPlatform.Registry.Secret - customConfig[configKeyRegistryAddress] = platform.Spec.BuildPlatform.Registry.Address - return customConfig, nil -} - // GetCommonConfigMap retrieves the config map with the builder common configuration information func GetCommonConfigMap(client client.Client, fallbackNS string) (*corev1.ConfigMap, error) { namespace, found := os.LookupEnv(envVarPodNamespaceName) diff --git a/controllers/builder/containerbuilder.go b/controllers/builder/containerbuilder.go index d907f9758..ece0df3f5 100644 --- a/controllers/builder/containerbuilder.go +++ b/controllers/builder/containerbuilder.go @@ -68,7 +68,6 @@ func (c *containerBuilderManager) Schedule(build *operatorapi.SonataFlowBuild) e } build.Status.BuildPhase = operatorapi.BuildPhase(containerBuilder.Status.Phase) build.Status.Error = containerBuilder.Status.Error - build.Status.ImageTag = imageNameTag return nil } @@ -84,6 +83,7 @@ func (c *containerBuilderManager) Reconcile(build *operatorapi.SonataFlowBuild) } build.Status.BuildPhase = operatorapi.BuildPhase(containerBuild.Status.Phase) build.Status.Error = containerBuild.Status.Error + build.Status.ImageTag = containerBuild.Status.RepositoryImageTag if err = build.Status.SetInnerBuild(containerBuild); err != nil { return err } @@ -104,8 +104,8 @@ func (c *containerBuilderManager) getImageBuilderForKaniko(workflowID string, im ib.WithPodMiddleName(workflowID) ib.WithInsecureRegistry(false) ib.WithImageNameTag(imageNameTag) - ib.WithSecret(c.platform.Spec.BuildPlatform.Registry.Secret) - ib.WithRegistryAddress(c.platform.Spec.BuildPlatform.Registry.Address) + ib.WithSecret(c.platform.Spec.Build.Config.Registry.Secret) + ib.WithRegistryAddress(c.platform.Spec.Build.Config.Registry.Address) ib.WithCache(task.Cache) ib.WithResources(task.Resources) ib.WithAdditionalFlags(task.AdditionalFlags) @@ -152,35 +152,6 @@ func (c *containerBuilderManager) buildImage(kb internalBuilder) (*api.Container return build, err } -func (c *containerBuilderManager) scheduleBuild(kb internalBuilder) (*api.ContainerBuild, error) { - cli, err := client.FromCtrlClientSchemeAndConfig(c.client, c.client.Scheme(), c.restConfig) - plat := api.PlatformContainerBuild{ - ObjectReference: api.ObjectReference{ - Namespace: kb.Namespace, - Name: kb.PodMiddleName, - }, - Spec: api.PlatformContainerBuildSpec{ - BuildStrategy: api.ContainerBuildStrategyPod, - PublishStrategy: api.PlatformBuildPublishStrategyKaniko, - Registry: api.ContainerRegistrySpec{ - Insecure: kb.InsecureRegistry, - Address: kb.RegistryAddress, - Secret: kb.Secret, - }, - Timeout: &metav1.Duration{ - Duration: kb.Timeout, - }, - }, - } - - build, err := newBuild(kb, plat, c.commonConfig.Data[configKeyDefaultExtension], cli) - if err != nil { - klog.V(log.E).ErrorS(err, "error during schedule build") - return nil, err - } - return build, err -} - // Helper function to create a new container-builder build and schedule it func newBuild(kb internalBuilder, platform api.PlatformContainerBuild, defaultExtension string, cli client.Client) (*api.ContainerBuild, error) { buildInfo := builder.ContainerBuilderInfo{FinalImageName: kb.ImageName, BuildUniqueName: kb.PodMiddleName, Platform: platform} diff --git a/controllers/builder/kogitoserverlessbuild_manager.go b/controllers/builder/kogitoserverlessbuild_manager.go index 5b15e5fb9..88eba14ca 100644 --- a/controllers/builder/kogitoserverlessbuild_manager.go +++ b/controllers/builder/kogitoserverlessbuild_manager.go @@ -49,7 +49,7 @@ func (k *sonataFlowBuildManager) GetOrCreateBuild(workflow *operatorapi.SonataFl if plat, err = platform.GetActivePlatform(k.ctx, k.client, workflow.Namespace); err != nil { return nil, err } - buildInstance.Spec.BuildTemplate = plat.Spec.BuildTemplate + buildInstance.Spec.BuildTemplate = plat.Spec.Build.Template if err = controllerutil.SetControllerReference(workflow, buildInstance, k.client.Scheme()); err != nil { return nil, err } diff --git a/controllers/builder/openshiftbuilder.go b/controllers/builder/openshiftbuilder.go index babce9099..50bfcfaed 100644 --- a/controllers/builder/openshiftbuilder.go +++ b/controllers/builder/openshiftbuilder.go @@ -106,8 +106,7 @@ func (o *openshiftBuilderManager) Schedule(build *operatorapi.SonataFlowBuild) e if err != nil { return err } - build.Status.ImageTag = workflowdef.GetWorkflowAppImageNameTag(workflow) - bc := o.newDefaultBuildConfig(build) + bc := o.newDefaultBuildConfig(build, workflow) if err = o.addExternalResources(bc, workflow); err != nil { return err } @@ -131,7 +130,7 @@ func (o *openshiftBuilderManager) Schedule(build *operatorapi.SonataFlowBuild) e if kubeutil.IsObjectNew(bc) { return nil } - referenceBC := o.newDefaultBuildConfig(build) + referenceBC := o.newDefaultBuildConfig(build, workflow) bc.Spec = *referenceBC.Spec.DeepCopy() return o.addExternalResources(bc, workflow) }); err != nil { @@ -142,7 +141,7 @@ func (o *openshiftBuilderManager) Schedule(build *operatorapi.SonataFlowBuild) e return nil } -func (o *openshiftBuilderManager) newDefaultBuildConfig(build *operatorapi.SonataFlowBuild) *buildv1.BuildConfig { +func (o *openshiftBuilderManager) newDefaultBuildConfig(build *operatorapi.SonataFlowBuild, workflow *operatorapi.SonataFlow) *buildv1.BuildConfig { optimizationPol := buildv1.ImageOptimizationSkipLayers dockerFile := platform.GetCustomizedDockerfile(o.commonConfig.Data[o.commonConfig.Data[configKeyDefaultBuilderResourceName]], *o.platform) return &buildv1.BuildConfig{ @@ -165,7 +164,7 @@ func (o *openshiftBuilderManager) newDefaultBuildConfig(build *operatorapi.Sonat Output: buildv1.BuildOutput{ To: &corev1.ObjectReference{ Namespace: build.Namespace, - Name: build.Status.ImageTag, + Name: workflowdef.GetWorkflowAppImageNameTag(workflow), Kind: imageStreamTagKind, }, }, @@ -218,6 +217,7 @@ func (o *openshiftBuilderManager) Reconcile(build *operatorapi.SonataFlowBuild) return err } build.Status.BuildPhase = operatorapi.BuildPhaseScheduling + build.Status.ImageTag = openshiftBuild.Status.OutputDockerImageReference return build.Status.SetInnerBuild(kubeutil.ToTypedLocalReference(openshiftBuild)) } @@ -234,6 +234,7 @@ func (o *openshiftBuilderManager) Reconcile(build *operatorapi.SonataFlowBuild) if openshiftBuild.Status.Phase == buildv1.BuildPhaseError { build.Status.Error = openshiftBuild.Status.Message } + build.Status.ImageTag = openshiftBuild.Status.OutputDockerImageReference return build.Status.SetInnerBuild(kubeutil.ToTypedLocalReference(openshiftBuild)) } diff --git a/controllers/platform/create.go b/controllers/platform/create.go index 9749834ff..e1afd9d3a 100644 --- a/controllers/platform/create.go +++ b/controllers/platform/create.go @@ -17,6 +17,7 @@ package platform import ( "context" + "github.com/kiegroup/kogito-serverless-operator/api" v08 "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08" ) @@ -34,12 +35,12 @@ func (action *createAction) Name() string { } func (action *createAction) CanHandle(platform *v08.SonataFlowPlatform) bool { - return platform.Status.Phase == v08.PlatformPhaseCreating + return platform.Status.IsCreating() } func (action *createAction) Handle(ctx context.Context, platform *v08.SonataFlowPlatform) (*v08.SonataFlowPlatform, error) { //TODO: Perform the actions needed for the Platform creation - platform.Status.Phase = v08.PlatformPhaseReady + platform.Status.Manager().MarkTrue(api.SucceedConditionType) return platform, nil } diff --git a/controllers/platform/defaults.go b/controllers/platform/defaults.go index a6da66cb9..f9f2c1903 100644 --- a/controllers/platform/defaults.go +++ b/controllers/platform/defaults.go @@ -18,7 +18,6 @@ import ( "context" "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kiegroup/kogito-serverless-operator/container-builder/client" @@ -32,11 +31,11 @@ func ConfigureDefaults(ctx context.Context, c client.Client, p *operatorapi.Sona // update missing fields in the resource if p.Status.Cluster == "" || utils.IsOpenShift() { p.Status.Cluster = operatorapi.PlatformClusterOpenShift - p.Spec.BuildPlatform.BuildStrategy = operatorapi.PlatformBuildStrategy + p.Spec.Build.Config.BuildStrategy = operatorapi.PlatformBuildStrategy } if p.Status.Cluster == "" || !utils.IsOpenShift() { p.Status.Cluster = operatorapi.PlatformClusterKubernetes - p.Spec.BuildPlatform.BuildStrategy = operatorapi.OperatorBuildStrategy + p.Spec.Build.Config.BuildStrategy = operatorapi.OperatorBuildStrategy } err := SetPlatformDefaults(p, verbose) @@ -49,8 +48,8 @@ func ConfigureDefaults(ctx context.Context, c client.Client, p *operatorapi.Sona return err } - if verbose && p.Spec.BuildPlatform.Timeout.Duration != 0 { - klog.V(log.I).InfoS("Maven Timeout set", "timeout", p.Spec.BuildPlatform.Timeout.Duration) + if verbose && p.Spec.Build.Config.Timeout.Duration != 0 { + klog.V(log.I).InfoS("Maven Timeout set", "timeout", p.Spec.Build.Config.Timeout.Duration) } updatePlatform(ctx, c, p) diff --git a/controllers/platform/initialize.go b/controllers/platform/initialize.go index a1e022ff9..01af2082e 100644 --- a/controllers/platform/initialize.go +++ b/controllers/platform/initialize.go @@ -24,6 +24,8 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/kiegroup/kogito-serverless-operator/api" + "github.com/kiegroup/kogito-serverless-operator/api/metadata" "github.com/kiegroup/kogito-serverless-operator/container-builder/client" @@ -51,7 +53,7 @@ func (action *initializeAction) Name() string { } func (action *initializeAction) CanHandle(platform *operatorapi.SonataFlowPlatform) bool { - return platform.Status.Phase == "" || platform.Status.Phase == operatorapi.PlatformPhaseDuplicate + return platform.Status.GetTopLevelCondition().IsUnknown() || platform.Status.IsDuplicated() } func (action *initializeAction) Handle(ctx context.Context, platform *operatorapi.SonataFlowPlatform) (*operatorapi.SonataFlowPlatform, error) { @@ -61,10 +63,9 @@ func (action *initializeAction) Handle(ctx context.Context, platform *operatorap } if duplicate { // another platform already present in the namespace - if platform.Status.Phase != operatorapi.PlatformPhaseDuplicate { + if !platform.Status.IsDuplicated() { plat := platform.DeepCopy() - plat.Status.Phase = operatorapi.PlatformPhaseDuplicate - + plat.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformDuplicatedReason, "") return plat, nil } @@ -75,7 +76,7 @@ func (action *initializeAction) Handle(ctx context.Context, platform *operatorap return nil, err } // nolint: staticcheck - if platform.Spec.BuildPlatform.BuildStrategy == operatorapi.OperatorBuildStrategy { + if platform.Spec.Build.Config.BuildStrategy == operatorapi.OperatorBuildStrategy { //If KanikoCache is enabled if IsKanikoCacheEnabled(platform) { // Create the persistent volume claim used by the Kaniko cache @@ -90,13 +91,13 @@ func (action *initializeAction) Handle(ctx context.Context, platform *operatorap if err != nil { return nil, err } - platform.Status.Phase = operatorapi.PlatformPhaseWarming + platform.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformWarmingReason, "") } else { // Skip the warmer pod creation - platform.Status.Phase = operatorapi.PlatformPhaseCreating + platform.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformCreatingReason, "") } } else { - platform.Status.Phase = operatorapi.PlatformPhaseCreating + platform.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformCreatingReason, "") } platform.Status.Version = metadata.SpecVersion @@ -112,7 +113,7 @@ func createPersistentVolumeClaim(ctx context.Context, client client.Client, plat } // nolint: staticcheck pvcName := defaultKanikoCachePVCName - if persistentVolumeClaim, found := platform.Spec.BuildPlatform.BuildStrategyOptions[kanikoPVCName]; found { + if persistentVolumeClaim, found := platform.Spec.Build.Config.BuildStrategyOptions[kanikoPVCName]; found { pvcName = persistentVolumeClaim } diff --git a/controllers/platform/kaniko_cache.go b/controllers/platform/kaniko_cache.go index 4c8a04c78..dd396860a 100644 --- a/controllers/platform/kaniko_cache.go +++ b/controllers/platform/kaniko_cache.go @@ -37,7 +37,7 @@ const kanikoBuildCacheEnabled = "KanikoBuildCacheEnabled" const kanikoDefaultWarmerImageName = "gcr.io/kaniko-project/warmer" func IsKanikoCacheEnabled(platform *v08.SonataFlowPlatform) bool { - return platform.Spec.BuildPlatform.IsOptionEnabled(kanikoBuildCacheEnabled) + return platform.Spec.Build.Config.IsStrategyOptionEnabled(kanikoBuildCacheEnabled) } func createKanikoCacheWarmerPod(ctx context.Context, client client.Client, platform *v08.SonataFlowPlatform) error { @@ -49,12 +49,12 @@ func createKanikoCacheWarmerPod(ctx context.Context, client client.Client, platf // - https://kubernetes.io/docs/concepts/storage/volumes/#local // nolint: staticcheck pvcName := defaultKanikoCachePVCName - if persistentVolumeClaim, found := platform.Spec.BuildPlatform.BuildStrategyOptions[kanikoPVCName]; found { + if persistentVolumeClaim, found := platform.Spec.Build.Config.BuildStrategyOptions[kanikoPVCName]; found { pvcName = persistentVolumeClaim } var warmerImage string - if image, found := platform.Spec.BuildPlatform.BuildStrategyOptions[kanikoWarmerImage]; found { + if image, found := platform.Spec.Build.Config.BuildStrategyOptions[kanikoWarmerImage]; found { warmerImage = image } else { warmerImage = fmt.Sprintf("%s:v%s", kanikoDefaultWarmerImageName, defaults.KanikoVersion) @@ -80,7 +80,7 @@ func createKanikoCacheWarmerPod(ctx context.Context, client client.Client, platf Args: []string{ "--force", "--cache-dir=" + kanikoCacheDir, - "--image=" + platform.Spec.BuildPlatform.BaseImage, + "--image=" + platform.Spec.Build.Config.BaseImage, }, VolumeMounts: []corev1.VolumeMount{ { diff --git a/controllers/platform/monitor.go b/controllers/platform/monitor.go index c3b928568..32e8c4d1a 100644 --- a/controllers/platform/monitor.go +++ b/controllers/platform/monitor.go @@ -38,7 +38,7 @@ func (action *monitorAction) Name() string { } func (action *monitorAction) CanHandle(platform *operatorapi.SonataFlowPlatform) bool { - return platform.Status.Phase == operatorapi.PlatformPhaseReady + return platform.Status.IsReady() } func (action *monitorAction) Handle(ctx context.Context, platform *operatorapi.SonataFlowPlatform) (*operatorapi.SonataFlowPlatform, error) { diff --git a/controllers/platform/platform.go b/controllers/platform/platform.go index f252d3ff0..7e63d5547 100644 --- a/controllers/platform/platform.go +++ b/controllers/platform/platform.go @@ -174,7 +174,7 @@ func ListAllPlatforms(ctx context.Context, c ctrl.Reader, namespace string) (*op // IsActive determines if the given platform is being used. func IsActive(p *operatorapi.SonataFlowPlatform) bool { - return p.Status.Phase != "" && p.Status.Phase != operatorapi.PlatformPhaseDuplicate + return !p.Status.IsDuplicated() } // IsSecondary determines if the given platform is marked as secondary. diff --git a/controllers/platform/platformutils.go b/controllers/platform/platformutils.go index 983eb8eb5..03cc47147 100644 --- a/controllers/platform/platformutils.go +++ b/controllers/platform/platformutils.go @@ -21,11 +21,10 @@ import ( "strings" "time" - "k8s.io/klog/v2" - corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/yaml" @@ -44,64 +43,63 @@ var builderDockerfileFromRE = regexp.MustCompile(`FROM (.*) AS builder`) type ResourceCustomizer func(object ctrl.Object) ctrl.Object func ConfigureRegistry(ctx context.Context, c client.Client, p *operatorapi.SonataFlowPlatform, verbose bool) error { - //@TODO Add a notification on the status about this registry value ignored when https://issues.redhat.com/browse/KOGITO-9218 will be implemented - if p.Spec.BuildPlatform.BuildStrategy == operatorapi.PlatformBuildStrategy && p.Status.Cluster == operatorapi.PlatformClusterOpenShift { - p.Spec.BuildPlatform.Registry = operatorapi.RegistrySpec{} - klog.V(log.I).InfoS("Platform registry not set and ignored on openshift cluster") + if p.Spec.Build.Config.BuildStrategy == operatorapi.PlatformBuildStrategy && p.Status.Cluster == operatorapi.PlatformClusterOpenShift { + p.Spec.Build.Config.Registry = operatorapi.RegistrySpec{} + klog.V(log.D).InfoS("Platform registry not set and ignored on openshift cluster") return nil } - if p.Spec.BuildPlatform.Registry.Address == "" && p.Status.Cluster == operatorapi.PlatformClusterKubernetes { + if p.Spec.Build.Config.Registry.Address == "" && p.Status.Cluster == operatorapi.PlatformClusterKubernetes { // try KEP-1755 address, err := GetRegistryAddress(ctx, c) if err != nil && verbose { klog.V(log.E).ErrorS(err, "Cannot find a registry where to push images via KEP-1755") } else if err == nil && address != nil { - p.Spec.BuildPlatform.Registry.Address = *address + p.Spec.Build.Config.Registry.Address = *address } } - klog.V(log.D).InfoS("Final Registry Address", "address", p.Spec.BuildPlatform.Registry.Address) + klog.V(log.D).InfoS("Final Registry Address", "address", p.Spec.Build.Config.Registry.Address) return nil } func SetPlatformDefaults(p *operatorapi.SonataFlowPlatform, verbose bool) error { - if p.Spec.BuildPlatform.BuildStrategyOptions == nil { + if p.Spec.Build.Config.BuildStrategyOptions == nil { klog.V(log.D).InfoS("SonataFlow Platform: setting publish strategy options", "namespace", p.Namespace) - p.Spec.BuildPlatform.BuildStrategyOptions = map[string]string{} + p.Spec.Build.Config.BuildStrategyOptions = map[string]string{} } - if p.Spec.BuildPlatform.GetTimeout().Duration != 0 { - d := p.Spec.BuildPlatform.GetTimeout().Duration.Truncate(time.Second) + if p.Spec.Build.Config.GetTimeout().Duration != 0 { + d := p.Spec.Build.Config.GetTimeout().Duration.Truncate(time.Second) - if verbose && p.Spec.BuildPlatform.Timeout.Duration != d { - klog.V(log.I).InfoS("ContainerBuild timeout minimum unit is sec", "configured", p.Spec.BuildPlatform.GetTimeout().Duration, "truncated", d) + if verbose && p.Spec.Build.Config.Timeout.Duration != d { + klog.V(log.I).InfoS("ContainerBuild timeout minimum unit is sec", "configured", p.Spec.Build.Config.GetTimeout().Duration, "truncated", d) } klog.V(log.D).InfoS("SonataFlow Platform: setting build timeout", "namespace", p.Namespace) - p.Spec.BuildPlatform.Timeout = &metav1.Duration{ + p.Spec.Build.Config.Timeout = &metav1.Duration{ Duration: d, } } else { klog.V(log.D).InfoS("SonataFlow Platform setting default build timeout to 5 minutes", "namespace", p.Namespace) - p.Spec.BuildPlatform.Timeout = &metav1.Duration{ + p.Spec.Build.Config.Timeout = &metav1.Duration{ Duration: 5 * time.Minute, } } - if p.Spec.BuildPlatform.IsOptionEnabled(kanikoBuildCacheEnabled) { - p.Spec.BuildPlatform.BuildStrategyOptions[kanikoPVCName] = p.Name - if len(p.Spec.BuildPlatform.BaseImage) == 0 { - p.Spec.BuildPlatform.BaseImage = workflowdef.GetDefaultWorkflowBuilderImageTag() + if p.Spec.Build.Config.IsStrategyOptionEnabled(kanikoBuildCacheEnabled) { + p.Spec.Build.Config.BuildStrategyOptions[kanikoPVCName] = p.Name + if len(p.Spec.Build.Config.BaseImage) == 0 { + p.Spec.Build.Config.BaseImage = workflowdef.GetDefaultWorkflowBuilderImageTag() } } - if p.Spec.BuildPlatform.BuildStrategy == operatorapi.OperatorBuildStrategy && !p.Spec.BuildPlatform.IsOptionEnabled(kanikoBuildCacheEnabled) { + if p.Spec.Build.Config.BuildStrategy == operatorapi.OperatorBuildStrategy && !p.Spec.Build.Config.IsStrategyOptionEnabled(kanikoBuildCacheEnabled) { // Default to disabling Kaniko cache warmer // Using the cache warmer pod seems unreliable with the current Kaniko version // and requires relying on a persistent volume. defaultKanikoBuildCache := "false" - p.Spec.BuildPlatform.BuildStrategyOptions[kanikoBuildCacheEnabled] = defaultKanikoBuildCache + p.Spec.Build.Config.BuildStrategyOptions[kanikoBuildCacheEnabled] = defaultKanikoBuildCache if verbose { klog.V(log.I).InfoS("Kaniko cache set", "value", defaultKanikoBuildCache) } @@ -110,8 +108,8 @@ func SetPlatformDefaults(p *operatorapi.SonataFlowPlatform, verbose bool) error setStatusAdditionalInfo(p) if verbose { - klog.V(log.I).InfoS("BaseImage set", "value", p.Spec.BuildPlatform.BaseImage) - klog.V(log.I).InfoS("Timeout set", "value", p.Spec.BuildPlatform.GetTimeout()) + klog.V(log.I).InfoS("BaseImage set", "value", p.Spec.Build.Config.BaseImage) + klog.V(log.I).InfoS("Timeout set", "value", p.Spec.Build.Config.GetTimeout()) } return nil } @@ -120,7 +118,7 @@ func setStatusAdditionalInfo(platform *operatorapi.SonataFlowPlatform) { platform.Status.Info = make(map[string]string) klog.V(log.D).InfoS("SonataFlow Platform setting build publish strategy", "namespace", platform.Namespace) - if platform.Spec.BuildPlatform.BuildStrategy == operatorapi.OperatorBuildStrategy { + if platform.Spec.Build.Config.BuildStrategy == operatorapi.OperatorBuildStrategy { platform.Status.Info["kanikoVersion"] = defaults.KanikoVersion } klog.V(log.D).InfoS("SonataFlow setting status info", "namespace", platform.Namespace) @@ -150,9 +148,9 @@ func GetRegistryAddress(ctx context.Context, c client.Client) (*string, error) { } func GetCustomizedDockerfile(dockerfile string, platform operatorapi.SonataFlowPlatform) string { - if platform.Spec.BuildPlatform.BaseImage != "" { + if platform.Spec.Build.Config.BaseImage != "" { res := builderDockerfileFromRE.FindAllStringSubmatch(dockerfile, 1) - dockerfile = strings.Replace(dockerfile, strings.Trim(res[0][1], " "), platform.Spec.BuildPlatform.BaseImage, 1) + dockerfile = strings.Replace(dockerfile, strings.Trim(res[0][1], " "), platform.Spec.Build.Config.BaseImage, 1) } return dockerfile } diff --git a/controllers/platform/platformutils_test.go b/controllers/platform/platformutils_test.go index 1bff3a2df..d0fcd851c 100644 --- a/controllers/platform/platformutils_test.go +++ b/controllers/platform/platformutils_test.go @@ -38,7 +38,7 @@ func TestSonataFlowBuildController(t *testing.T) { assert.True(t, foundDefault) // 2 - Let's try to override using the productized image - platform.Spec.BuildPlatform.BaseImage = "registry.access.redhat.com/openshift-serverless-1-tech-preview/logic-swf-builder-rhel8" + platform.Spec.Build.Config.BaseImage = "registry.access.redhat.com/openshift-serverless-1-tech-preview/logic-swf-builder-rhel8" resProductized := GetCustomizedDockerfile(dockerfile, *platform) foundProductized, err := regexp.MatchString("FROM registry.access.redhat.com/openshift-serverless-1-tech-preview/logic-swf-builder-rhel8 AS builder", resProductized) assert.NoError(t, err) diff --git a/controllers/platform/warm.go b/controllers/platform/warm.go index 2a756fa10..75680514d 100644 --- a/controllers/platform/warm.go +++ b/controllers/platform/warm.go @@ -18,13 +18,14 @@ import ( "context" "errors" - "k8s.io/klog/v2" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime/pkg/client" + "github.com/kiegroup/kogito-serverless-operator/api" + operatorapi "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08" "github.com/kiegroup/kogito-serverless-operator/log" ) @@ -45,7 +46,7 @@ func (action *warmAction) Name() string { } func (action *warmAction) CanHandle(platform *operatorapi.SonataFlowPlatform) bool { - return platform.Status.Phase == operatorapi.PlatformPhaseWarming + return platform.Status.IsWarming() } func (action *warmAction) Handle(ctx context.Context, platform *operatorapi.SonataFlowPlatform) (*operatorapi.SonataFlowPlatform, error) { @@ -68,8 +69,8 @@ func (action *warmAction) Handle(ctx context.Context, platform *operatorapi.Sona switch pod.Status.Phase { case corev1.PodSucceeded: - klog.V(log.I).InfoS("Kaniko cache successfully warmed up") - platform.Status.Phase = operatorapi.PlatformPhaseCreating + klog.V(log.D).InfoS("Kaniko cache successfully warmed up") + platform.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformWarmingReason, "Kaniko cache successfully warmed up") return platform, nil case corev1.PodFailed: return nil, errors.New("failed to warm up Kaniko cache") diff --git a/controllers/profiles/reconciler_dev.go b/controllers/profiles/reconciler_dev.go index 353ff1cbe..ebca258cd 100644 --- a/controllers/profiles/reconciler_dev.go +++ b/controllers/profiles/reconciler_dev.go @@ -186,8 +186,8 @@ func (e *ensureRunningDevWorkflowReconciliationState) Do(ctx context.Context, wo devBaseContainerImage := workflowdef.GetDefaultWorkflowDevModeImageTag() pl, errPl := platform.GetActivePlatform(ctx, e.client, workflow.Namespace) // check if the Platform available - if errPl == nil && len(pl.Spec.DevBaseImage) > 0 { - devBaseContainerImage = pl.Spec.DevBaseImage + if errPl == nil && len(pl.Spec.DevMode.BaseImage) > 0 { + devBaseContainerImage = pl.Spec.DevMode.BaseImage } deployment, _, err := e.ensurers.deployment.ensure(ctx, workflow, diff --git a/controllers/profiles/reconciler_prod.go b/controllers/profiles/reconciler_prod.go index adb702929..8fc088e45 100644 --- a/controllers/profiles/reconciler_prod.go +++ b/controllers/profiles/reconciler_prod.go @@ -30,8 +30,6 @@ import ( "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "github.com/kiegroup/kogito-serverless-operator/controllers/workflowdef" - appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -190,24 +188,27 @@ func (h *deployWorkflowReconciliationState) CanReconcile(workflow *operatorapi.S } func (h *deployWorkflowReconciliationState) Do(ctx context.Context, workflow *operatorapi.SonataFlow) (ctrl.Result, []client.Object, error) { - pl, err := platform.GetActivePlatform(ctx, h.client, workflow.Namespace) + // Guard to avoid errors while getting a new builder manager. + // Maybe we can do typed errors in the buildManager and + // have something like sonataerr.IsPlatformNotFound(err) instead. + _, err := platform.GetActivePlatform(ctx, h.client, workflow.Namespace) if err != nil { workflow.Status.Manager().MarkFalse(api.RunningConditionType, api.WaitingForPlatformReason, "No active Platform for namespace %s so the resWorkflowDef cannot be deployed. Waiting for an active platform", workflow.Namespace) return ctrl.Result{RequeueAfter: requeueWhileWaitForPlatform}, nil, err } + buildManager := builder.NewSonataFlowBuildManager(ctx, h.client) + build, err := buildManager.GetOrCreateBuild(workflow) + if err != nil { + return ctrl.Result{}, nil, err + } + if h.isWorkflowChanged(workflow) { // Let's check that the 2 resWorkflowDef definition are different workflow.Status.Manager().MarkUnknown(api.RunningConditionType, "", "") - buildManager := builder.NewSonataFlowBuildManager(ctx, h.client) - build, err := buildManager.GetOrCreateBuild(workflow) - if err != nil { - return ctrl.Result{}, nil, err - } if err = buildManager.MarkToRestart(build); err != nil { return ctrl.Result{}, nil, err } - workflow.Status.Manager().MarkFalse(api.BuiltConditionType, api.BuildIsRunningReason, "Marked to restart") workflow.Status.Manager().MarkUnknown(api.RunningConditionType, "", "") _, err = h.performStatusUpdate(ctx, workflow) @@ -215,11 +216,7 @@ func (h *deployWorkflowReconciliationState) Do(ctx context.Context, workflow *op } // didn't change, business as usual - image := workflowdef.GetWorkflowAppImageNameTag(workflow) - if len(pl.Spec.BuildPlatform.Registry.Address) > 0 { - image = pl.Spec.BuildPlatform.Registry.Address + "/" + image - } - return h.handleObjects(ctx, workflow, image) + return h.handleObjects(ctx, workflow, build.Status.ImageTag) } func (h *deployWorkflowReconciliationState) handleObjects(ctx context.Context, workflow *operatorapi.SonataFlow, image string) (reconcile.Result, []client.Object, error) { diff --git a/controllers/profiles/reconciler_prod_test.go b/controllers/profiles/reconciler_prod_test.go index f3df43d28..98bc03994 100644 --- a/controllers/profiles/reconciler_prod_test.go +++ b/controllers/profiles/reconciler_prod_test.go @@ -83,7 +83,11 @@ func Test_reconcilerProdBuildConditions(t *testing.T) { func Test_deployWorkflowReconciliationHandler_handleObjects(t *testing.T) { workflow := test.GetBaseSonataFlow(t.Name()) platform := test.GetBasePlatformInReadyPhase(t.Name()) - client := test.NewKogitoClientBuilder().WithRuntimeObjects(workflow, platform).WithStatusSubresource(workflow, platform).Build() + build := test.GetLocalSucceedSonataFlowBuild(workflow.Name, workflow.Namespace) + client := test.NewKogitoClientBuilder(). + WithRuntimeObjects(workflow, platform, build). + WithStatusSubresource(workflow, platform, build). + Build() handler := &deployWorkflowReconciliationState{ stateSupport: fakeReconcilerSupport(client), ensurers: newProdObjectEnsurers(&stateSupport{client: client}), diff --git a/controllers/profiles/testdata/sonataflow.org_v1alpha08_sonataflowbuild.yaml b/controllers/profiles/testdata/sonataflow.org_v1alpha08_sonataflowbuild.yaml new file mode 100644 index 000000000..060911822 --- /dev/null +++ b/controllers/profiles/testdata/sonataflow.org_v1alpha08_sonataflowbuild.yaml @@ -0,0 +1,47 @@ +# Copyright 2023 Red Hat, Inc. and/or its affiliates +# +# Licensed 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. + +apiVersion: sonataflow.org/v1alpha08 +kind: SonataFlowBuild +metadata: + name: greeting +spec: + resources: {} + timeout: 0s +status: + buildPhase: Succeeded + imageTag: 10.100.163.129/greeting:0.0.1 + innerBuild: + metadata: + name: greeting + spec: + strategy: pod + tasks: + - kaniko: + cache: {} + contextDir: /builder/greeting/context + image: greeting:0.0.1 + name: KanikoTask + registry: + address: 10.100.163.129 + resources: {} + timeout: 5m0s + status: + duration: 2m49s + phase: Succeeded + repositoryImageTag: 10.100.163.129/greeting:0.0.1 + resourceVolume: + referenceName: sonataflow-greeting-builder + referenceType: configMap + startedAt: "2023-08-03T16:11:47Z" diff --git a/controllers/sonataflow_controller.go b/controllers/sonataflow_controller.go index 335ed67f4..f4a8a0549 100644 --- a/controllers/sonataflow_controller.go +++ b/controllers/sonataflow_controller.go @@ -95,7 +95,7 @@ func (r *SonataFlowReconciler) Reconcile(ctx context.Context, req ctrl.Request) func platformEnqueueRequestsFromMapFunc(c client.Client, p *operatorapi.SonataFlowPlatform) []reconcile.Request { var requests []reconcile.Request - if p.Status.Phase == operatorapi.PlatformPhaseReady { + if p.Status.IsReady() { list := &operatorapi.SonataFlowList{} // Do global search in case of global operator (it may be using a global platform) @@ -134,12 +134,12 @@ func (r *SonataFlowReconciler) SetupWithManager(mgr ctrl.Manager) error { Owns(&corev1.ConfigMap{}). Owns(&operatorapi.SonataFlowBuild{}). Watches(&operatorapi.SonataFlowPlatform{}, handler.EnqueueRequestsFromMapFunc(func(c context.Context, a client.Object) []reconcile.Request { - platform, ok := a.(*operatorapi.SonataFlowPlatform) + plat, ok := a.(*operatorapi.SonataFlowPlatform) if !ok { klog.V(log.E).InfoS("Failed to retrieve workflow list. Type assertion failed", "assertion", a) return []reconcile.Request{} } - return platformEnqueueRequestsFromMapFunc(mgr.GetClient(), platform) + return platformEnqueueRequestsFromMapFunc(mgr.GetClient(), plat) })). Complete(r) } diff --git a/controllers/sonataflowbuild_controller.go b/controllers/sonataflowbuild_controller.go index 8ead7077c..67b8e17cb 100644 --- a/controllers/sonataflowbuild_controller.go +++ b/controllers/sonataflowbuild_controller.go @@ -17,6 +17,7 @@ package controllers import ( "context" "fmt" + "reflect" "time" "k8s.io/klog/v2" @@ -88,11 +89,11 @@ func (r *SonataFlowBuildReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{RequeueAfter: requeueAfterForNewBuild}, r.manageStatusUpdate(ctx, build) // TODO: this smells, why not just else? review in the future: https://issues.redhat.com/browse/KOGITO-8785 } else if phase != operatorapi.BuildPhaseSucceeded && phase != operatorapi.BuildPhaseError && phase != operatorapi.BuildPhaseFailed { - beforeReconcilePhase := build.Status.BuildPhase + beforeReconcileStatus := build.Status.DeepCopy() if err = buildManager.Reconcile(build); err != nil { return ctrl.Result{}, err } - if beforeReconcilePhase != build.Status.BuildPhase { + if !reflect.DeepEqual(build.Status, beforeReconcileStatus) { if err = r.manageStatusUpdate(ctx, build); err != nil { return ctrl.Result{}, err } diff --git a/controllers/sonataflowplatform_controller.go b/controllers/sonataflowplatform_controller.go index 330a9a456..b742b1059 100644 --- a/controllers/sonataflowplatform_controller.go +++ b/controllers/sonataflowplatform_controller.go @@ -19,16 +19,17 @@ import ( "fmt" "time" - "k8s.io/klog/v2" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" + "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/kiegroup/kogito-serverless-operator/api" + clientr "github.com/kiegroup/kogito-serverless-operator/container-builder/client" "github.com/kiegroup/kogito-serverless-operator/controllers/platform" @@ -89,6 +90,8 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx context.Context, req reconc return reconcile.Result{}, err } + instance.Status.Manager().InitializeConditions() + // Only process resources assigned to the operator if !platform.IsOperatorHandlerConsideringLock(ctx, r.Reader, req.Namespace, &instance) { klog.V(log.I).InfoS("Ignoring request because resource is not assigned to current operator") @@ -101,8 +104,6 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx context.Context, req reconc platform.NewMonitorAction(), } - var targetPhase operatorapi.PlatformPhase - var err error target := instance.DeepCopy() @@ -112,13 +113,16 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx context.Context, req reconc a.InjectClient(cli) if a.CanHandle(target) { - klog.V(log.I).InfoS("Invoking action", "Name", a.Name()) - phaseFrom := target.Status.Phase + klog.V(log.I).InfoS("Invoking action", "Name", a.Name()) target, err = a.Handle(ctx, target) if err != nil { - r.Recorder.Event(&instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated platform phase to %s", instance.Status.Phase)) + target.Status.Manager().MarkFalse(api.SucceedConditionType, operatorapi.PlatformFailureReason, err.Error()) + if err := r.Client.Status().Patch(ctx, target, ctrl.MergeFrom(&instance)); err != nil { + return reconcile.Result{}, err + } + r.Recorder.Event(&instance, corev1.EventTypeWarning, "Failed", fmt.Sprintf("Failed to update SonataFlowPlaform: %s", err)) return reconcile.Result{}, err } @@ -126,34 +130,24 @@ func (r *SonataFlowPlatformReconciler) Reconcile(ctx context.Context, req reconc target.Status.ObservedGeneration = instance.Generation if err := r.Client.Status().Patch(ctx, target, ctrl.MergeFrom(&instance)); err != nil { - r.Recorder.Event(&instance, corev1.EventTypeNormal, "Status Updated", fmt.Sprintf("Updated platform phase to %s", instance.Status.Phase)) + r.Recorder.Event(&instance, corev1.EventTypeNormal, "Status Updated", fmt.Sprintf("Updated platform condition %s", instance.Status.GetTopLevelCondition())) return reconcile.Result{}, err } if err := r.Client.Update(ctx, target); err != nil { - r.Recorder.Event(&instance, corev1.EventTypeNormal, "Spec Updated", fmt.Sprintf("Updated platform phase to %s", instance.Status.Phase)) + r.Recorder.Event(&instance, corev1.EventTypeNormal, "Spec Updated", fmt.Sprintf("Updated platform condition to %s", instance.Status.GetTopLevelCondition())) return reconcile.Result{}, err } - - targetPhase = target.Status.Phase - - if targetPhase != phaseFrom { - klog.V(log.I).InfoS( - "state transition", - "phase-from", phaseFrom, - "phase-to", target.Status.Phase, - ) - } } // handle one action at time so the resource // is always at its latest state - r.Recorder.Event(&instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated platform phase to %s", instance.Status.Phase)) + r.Recorder.Event(&instance, corev1.EventTypeNormal, "Updated", fmt.Sprintf("Updated platform condition to %s", instance.Status.GetTopLevelCondition())) break } } - if targetPhase == operatorapi.PlatformPhaseReady { + if target.Status.IsReady() { return reconcile.Result{}, nil } diff --git a/controllers/sonataflowplatform_controller_test.go b/controllers/sonataflowplatform_controller_test.go index af0c31666..8c2d6cdea 100644 --- a/controllers/sonataflowplatform_controller_test.go +++ b/controllers/sonataflowplatform_controller_test.go @@ -55,11 +55,11 @@ func TestSonataFlowPlatformController(t *testing.T) { assert.NoError(t, cl.Get(context.TODO(), types.NamespacedName{Name: ksp.Name, Namespace: ksp.Namespace}, ksp)) // Perform some checks on the created CR - assert.Equal(t, "quay.io/kiegroup", ksp.Spec.BuildPlatform.Registry.Address) - assert.Equal(t, "regcred", ksp.Spec.BuildPlatform.Registry.Secret) - assert.Equal(t, v1alpha08.OperatorBuildStrategy, ksp.Spec.BuildPlatform.BuildStrategy) + assert.Equal(t, "quay.io/kiegroup", ksp.Spec.Build.Config.Registry.Address) + assert.Equal(t, "regcred", ksp.Spec.Build.Config.Registry.Secret) + assert.Equal(t, v1alpha08.OperatorBuildStrategy, ksp.Spec.Build.Config.BuildStrategy) assert.Equal(t, v1alpha08.PlatformClusterKubernetes, ksp.Status.Cluster) - assert.Equal(t, v1alpha08.PlatformPhaseCreating, ksp.Status.Phase) + assert.Equal(t, v1alpha08.PlatformCreatingReason, ksp.Status.GetTopLevelCondition().Reason) }) } diff --git a/hack/local/run-operator.sh b/hack/local/run-operator.sh index 50b5a8f84..8b3ed077f 100755 --- a/hack/local/run-operator.sh +++ b/hack/local/run-operator.sh @@ -18,5 +18,6 @@ kubectl apply -f ./bundle/manifests/sonataflow.org_sonataflowplatforms.yaml kubectl apply -f ./bundle/manifests/sonataflow.org_sonataflowbuilds.yaml kubectl apply -f ./bundle/manifests/sonataflow.org_sonataflows.yaml +kubectl apply -f ./bundle/manifests/sonataflow-operator-builder-config_v1_configmap.yaml make debug \ No newline at end of file diff --git a/operator.yaml b/operator.yaml index 395cd527f..1b68e11b2 100644 --- a/operator.yaml +++ b/operator.yaml @@ -165,12 +165,12 @@ spec: - jsonPath: .status.cluster name: Cluster type: string - - jsonPath: .status.phase - name: Phase - type: string - - jsonPath: .status.phase=='Ready' + - jsonPath: .status.conditions[?(@.type=='Succeed')].status name: Ready type: string + - jsonPath: .status.conditions[?(@.type=='Succeed')].reason + name: Reason + type: string name: v1alpha08 schema: openAPIV3Schema: @@ -193,172 +193,132 @@ spec: description: SonataFlowPlatformSpec defines the desired state of SonataFlowPlatform properties: build: - description: BuildTemplate specify how to build the Workflow. It's - used as a template for the SonataFlowBuild + description: Attributes for building workflows in the target platform properties: - arguments: - description: Arguments lists the command line arguments to send - to the builder - items: - type: string - type: array - resources: - description: Resources optional compute resource requirements - for the builder + config: + description: Describes the platform configuration for building + workflows. properties: - claims: - description: "Claims lists the names of resources, defined - in spec.resourceClaims, that are used by this container. - \n This is an alpha field and requires enabling the DynamicResourceAllocation - feature gate. \n This field is immutable. It can only be - set for containers." - items: - description: ResourceClaim references one entry in PodSpec.ResourceClaims. - properties: - name: - description: Name must match the name of one entry in - pod.spec.resourceClaims of the Pod where this field - is used. It makes that resource available inside a - container. - type: string - required: - - name - type: object - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute - resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + baseImage: + description: a base image that can be used as base layer for + all images. It can be useful if you want to provide some + custom base image with further utility software + type: string + registry: + description: Registry the registry where to publish the built + image + properties: + address: + description: the URI to access + type: string + ca: + description: the configmap which stores the Certificate + Authority + type: string + insecure: + description: if the container registry is insecure (ie, + http only) + type: boolean + organization: + description: the registry organization + type: string + secret: + description: the secret where credentials are stored + type: string type: object - requests: + strategy: + description: BuildStrategy to use to build workflows in the + platform. Usually, the operator elect the strategy based + on the platform. Note that this field might be read only + in certain scenarios. + type: string + strategyOptions: additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. Requests cannot exceed - Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: string + description: BuildStrategyOptions additional options to add + to the build strategy. See https://sonataflow.org/serverlessworkflow/main/cloud/operator/build-and-deploy-workflows.html type: object + timeout: + description: how much time to wait before time out the build + process + type: string type: object - timeout: - description: Timeout defines the Build maximum execution duration. - The Build deadline is set to the Build start time plus the Timeout - duration. If the Build deadline is exceeded, the Build context - is canceled, and its phase set to BuildPhaseFailed. - format: duration - type: string - type: object - configuration: - description: Configuration list of configuration properties to be - attached to all the Workflow built from this Platform - properties: - type: - description: 'Type represents the type of configuration, ie: property, - configmap, secret, ...' - type: string - value: - description: Value a reference to the object for this configuration - (syntax may vary depending on the `Type`) + template: + description: Describes a build template for building workflows. + Base for the internal SonataFlowBuild resource. properties: - apiVersion: - description: API version of the referent. - type: string - fieldPath: - description: 'If referring to a piece of an object instead - of an entire object, this string should contain a valid - JSON/Go field access statement, such as desiredState.manifest.containers[2]. - For example, if the object reference is to a container within - a pod, this would take on a value like: "spec.containers{name}" - (where "name" refers to the name of the container that triggered - the event) or if no container name is specified "spec.containers[2]" - (container with index 2 in this pod). This syntax is chosen - only to have some well-defined way of referencing a part - of an object. TODO: this design is not final and this field - is subject to change in the future.' - type: string - kind: - description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - namespace: - description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/' - type: string - resourceVersion: - description: 'Specific resourceVersion to which this reference - is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency' - type: string - uid: - description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids' + arguments: + description: Arguments lists the command line arguments to + send to the builder + items: + type: string + type: array + resources: + description: Resources optional compute resource requirements + for the builder + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. \n This field + is immutable. It can only be set for containers." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + timeout: + description: Timeout defines the Build maximum execution duration. + The Build deadline is set to the Build start time plus the + Timeout duration. If the Build deadline is exceeded, the + Build context is canceled, and its phase set to BuildPhaseFailed. + format: duration type: string type: object - x-kubernetes-map-type: atomic - required: - - type - - value type: object - devBaseImage: - description: DevBaseImage Base image to run the Workflow in dev mode - instead of the operator's default. Optional, used for the dev profile - only - type: string - platform: - description: BuildPlatform specify how is the platform where we want - to build the Workflow + devMode: + description: Attributes for running workflows in devmode (immutable, + no build required) properties: baseImage: - description: a base image that can be used as base layer for all - images. It can be useful if you want to provide some custom - base image with further utility software - type: string - buildStrategy: - description: BuildStrategy to use to build workflows in the platform. - Usually, the operator elect the strategy based on the platform. - Note that this field might be read only in certain scenarios. - type: string - buildStrategyOptions: - additionalProperties: - type: string - description: 'TODO: add a link to the documentation where the - user can find more info about this field BuildStrategyOptions - additional options to add to the build strategy.' - type: object - registry: - description: Registry the registry where to publish the built - image - properties: - address: - description: the URI to access - type: string - ca: - description: the configmap which stores the Certificate Authority - type: string - insecure: - description: if the container registry is insecure (ie, http - only) - type: boolean - organization: - description: the registry organization - type: string - secret: - description: the secret where credentials are stored - type: string - type: object - timeout: - description: how much time to wait before time out the build process + description: Base image to run the Workflow in dev mode instead + of the operator's default. type: string type: object type: object @@ -373,17 +333,12 @@ spec: - openshift type: string conditions: - description: Conditions which are the conditions met (particularly - useful when in ERROR phase) + description: The latest available observations of a resource's current + state. items: - description: PlatformCondition describes the state of a resource - at a certain point. + description: Condition describes the common structure for conditions + in our types properties: - lastTransitionTime: - description: Last time the condition transitioned from one status - to another. - format: date-time - type: string lastUpdateTime: description: The last time this condition was updated. format: date-time @@ -399,7 +354,7 @@ spec: description: Status of the condition, one of True, False, Unknown. type: string type: - description: Type of platform condition (i.e. Kubernetes, OpenShift). + description: Type condition for the given object type: string required: - status @@ -412,13 +367,9 @@ spec: description: Info generic information related to the build type: object observedGeneration: - description: ObservedGeneration is the most recent generation observed - for this Platform. + description: The generation observed by the deployment controller. format: int64 type: integer - phase: - description: Phase defines in what phase the Platform is found - type: string version: description: Version the operator version controlling this Platform type: string diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml index af37fe431..74a371704 100644 --- a/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml +++ b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml @@ -17,5 +17,6 @@ kind: SonataFlowPlatform metadata: name: sonataflow-platform spec: - platform: - buildStrategy: platform \ No newline at end of file + build: + config: + strategy: platform \ No newline at end of file diff --git a/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml index 15149e2ba..f1226091a 100644 --- a/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml +++ b/test/testdata/sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml @@ -17,7 +17,8 @@ kind: SonataFlowPlatform metadata: name: sonataflow-platform spec: - platform: - baseImage: quay.io/kiegroup/kogito-swf-builder-nightly:latest - buildStrategyOptions: - KanikoBuildCacheEnabled: "true" + build: + config: + baseImage: quay.io/kiegroup/kogito-swf-builder-nightly:latest + strategyOptions: + KanikoBuildCacheEnabled: "true" diff --git a/test/yaml.go b/test/yaml.go index 7c6f2acb6..388b8f233 100644 --- a/test/yaml.go +++ b/test/yaml.go @@ -20,9 +20,10 @@ import ( "runtime" "strings" + "github.com/davecgh/go-spew/spew" "k8s.io/klog/v2" - "github.com/davecgh/go-spew/spew" + "github.com/kiegroup/kogito-serverless-operator/api" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -40,8 +41,7 @@ const ( sonataFlowPlatformWithCacheMinikubeYamlCR = "sonataflow.org_v1alpha08_sonataflowplatform_withCache_minikube.yaml" sonataFlowPlatformForOpenshift = "sonataflow.org_v1alpha08_sonataflowplatform_openshift.yaml" sonataFlowBuilderConfig = "sonataflow-operator-builder-config_v1_configmap.yaml" - - BuilderDockerfile = "builder_dockerfile.yaml" + sonataFlowBuildSucceed = "sonataflow.org_v1alpha08_sonataflowbuild.yaml" configSamplesOneLevelPath = "../config/samples/" configSamplesTwoLevelPath = "../../config/samples/" @@ -85,12 +85,13 @@ func GetSonataFlowPlatform(path string) *operatorapi.SonataFlowPlatform { panic(err) } klog.V(log.D).InfoS("Successfully read KSP", "ksp", ksp) + ksp.Status.Manager().InitializeConditions() return ksp } func GetSonataFlowPlatformInReadyPhase(path string, namespace string) *operatorapi.SonataFlowPlatform { ksp := GetSonataFlowPlatform(path) - ksp.Status.Phase = operatorapi.PlatformPhaseReady + ksp.Status.Manager().MarkTrue(api.SucceedConditionType) ksp.Namespace = namespace return ksp } @@ -109,7 +110,23 @@ func GetNewEmptySonataFlowBuild(name, namespace string) *operatorapi.SonataFlowB }, Status: operatorapi.SonataFlowBuildStatus{}, } +} +// GetLocalSucceedSonataFlowBuild gets a local (testdata dir ref to caller) SonataFlowBuild with Succeed status equals to true. +func GetLocalSucceedSonataFlowBuild(name, namespace string) *operatorapi.SonataFlowBuild { + yamlFile, err := os.ReadFile("testdata/" + sonataFlowBuildSucceed) + if err != nil { + klog.ErrorS(err, "Yaml file not found on local testdata dir") + panic(err) + } + build := &operatorapi.SonataFlowBuild{} + if err := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(yamlFile), 255).Decode(build); err != nil { + klog.ErrorS(err, "Failed to unmarshal SonataFlowBuild") + panic(err) + } + build.Name = name + build.Namespace = namespace + return build } func GetSonataFlowBuilderConfig(path, namespace string) *corev1.ConfigMap { @@ -181,16 +198,16 @@ func GetBasePlatformInReadyPhase(namespace string) *operatorapi.SonataFlowPlatfo func GetBasePlatformWithBaseImageInReadyPhase(namespace string) *operatorapi.SonataFlowPlatform { platform := GetBasePlatform() platform.Namespace = namespace - platform.Status.Phase = operatorapi.PlatformPhaseReady - platform.Spec.BuildPlatform.BaseImage = "quay.io/customx/custom-swf-builder:24.8.17" + platform.Status.Manager().MarkTrue(api.SucceedConditionType) + platform.Spec.Build.Config.BaseImage = "quay.io/customx/custom-swf-builder:24.8.17" return platform } func GetBasePlatformWithDevBaseImageInReadyPhase(namespace string) *operatorapi.SonataFlowPlatform { platform := GetBasePlatform() platform.Namespace = namespace - platform.Status.Phase = operatorapi.PlatformPhaseReady - platform.Spec.DevBaseImage = "quay.io/customgroup/custom-swf-builder-nightly:42.43.7" + platform.Status.Manager().MarkTrue(api.SucceedConditionType) + platform.Spec.DevMode.BaseImage = "quay.io/customgroup/custom-swf-builder-nightly:42.43.7" return platform } @@ -199,7 +216,9 @@ func GetBasePlatform() *operatorapi.SonataFlowPlatform { if ok { return GetSonataFlowPlatform(GetPathForSamples(file) + sonataFlowPlatformYamlCR) } else { - return &operatorapi.SonataFlowPlatform{} + ksp := &operatorapi.SonataFlowPlatform{} + ksp.Status.Manager().InitializeConditions() + return ksp } }