blob: 5a4e870bb83460bc8a35f85b8f64e7bba1f2a4c9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package v1alpha08
import (
"context"
"errors"
"path"
"regexp"
"strings"
cncfmodel "github.com/serverlessworkflow/sdk-go/v2/model"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
controllerruntime "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/yaml"
"github.com/apache/incubator-kie-kogito-serverless-operator/api/metadata"
)
var namingRegexp = regexp.MustCompile("^[a-z0-9](-?[a-z0-9])*$")
var allowedCharsRegexp = regexp.MustCompile("[^-a-z0-9]")
var startingDashRegexp = regexp.MustCompile("^-+")
var crdVersionRegexp = regexp.MustCompile("v[0-9](alpha|beta)?")
const (
// see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/
dash = "-"
charLimit = 253
)
// FromCNCFWorkflow converts the given CNCF Serverless Workflow instance in a new SonataFlow Custom Resource.
func FromCNCFWorkflow(cncfWorkflow *cncfmodel.Workflow, context context.Context) (*SonataFlow, error) {
if cncfWorkflow == nil {
return nil, errors.New("CNCF Workflow is nil")
}
workflowCR := &SonataFlow{
ObjectMeta: metav1.ObjectMeta{
Name: extractName(cncfWorkflow),
Annotations: map[string]string{
metadata.ExpressionLang: string(cncfWorkflow.ExpressionLang),
metadata.Version: cncfWorkflow.Version,
metadata.Description: cncfWorkflow.Description,
},
},
}
workflowBytes, err := yaml.Marshal(cncfWorkflow)
if err != nil {
return nil, err
}
workflowCRFlow := &Flow{}
if err = yaml.Unmarshal(workflowBytes, workflowCRFlow); err != nil {
return nil, err
}
workflowCR.Spec.Flow = *workflowCRFlow
s, _ := SchemeBuilder.Build()
gvks, _, err := s.ObjectKinds(workflowCR)
if err != nil {
return nil, err
}
for _, gvk := range gvks {
if len(gvk.Version) == 0 {
continue
}
workflowCR.SetGroupVersionKind(gvk)
}
warnIfSpecVersionNotSupported(cncfWorkflow, context)
return workflowCR, nil
}
// ToCNCFWorkflow converts a SonataFlow object to a Workflow one in order to be able to convert it to a YAML/Json
func ToCNCFWorkflow(workflowCR *SonataFlow, context context.Context) (*cncfmodel.Workflow, error) {
if workflowCR == nil {
return nil, errors.New("SonataFlow is nil")
}
cncfWorkflow := &cncfmodel.Workflow{}
workflowBytes, err := yaml.Marshal(workflowCR.Spec.Flow)
if err != nil {
return nil, err
}
if err = yaml.Unmarshal(workflowBytes, cncfWorkflow); err != nil {
return nil, err
}
cncfWorkflow.ID = workflowCR.ObjectMeta.Name
cncfWorkflow.Key = workflowCR.ObjectMeta.Annotations[metadata.Key]
cncfWorkflow.Description = workflowCR.ObjectMeta.Annotations[metadata.Description]
cncfWorkflow.Version = workflowCR.ObjectMeta.Annotations[metadata.Version]
cncfWorkflow.SpecVersion = extractSpecVersion(workflowCR)
cncfWorkflow.ExpressionLang = cncfmodel.ExpressionLangType(extractExpressionLang(workflowCR.ObjectMeta.Annotations))
warnIfSpecVersionNotSupported(cncfWorkflow, context)
return cncfWorkflow, nil
}
// warnIfSpecVersionNotSupported simple check if the version is not supported by the operator.
// Clearly this will be reviewed once we support 0.9.
func warnIfSpecVersionNotSupported(workflow *cncfmodel.Workflow, context context.Context) {
// simple guard to avoid polluting user's log.
if len(workflow.SpecVersion) == 0 {
workflow.SpecVersion = metadata.SpecVersion
return
}
if metadata.SpecVersion != workflow.SpecVersion {
controllerruntime.LoggerFrom(context).Info("SpecVersion not supported", "Workflow SpecVersion", workflow.Version)
}
}
func extractExpressionLang(annotations map[string]string) string {
expressionLang := annotations[metadata.ExpressionLang]
if expressionLang != "" {
return expressionLang
}
return metadata.DefaultExpressionLang
}
// Function to extract from the apiVersion the ServerlessWorkflow schema version
// For example given SonataFlow APIVersion, we would like to extract 0.8
func extractSpecVersion(workflowCR *SonataFlow) string {
schemaVersion := path.Base(workflowCR.APIVersion)
if len(schemaVersion) == 0 {
return metadata.SpecVersion
}
schemaVersion = crdVersionRegexp.ReplaceAllString(schemaVersion, "")
// we only support major minor from the spec
return schemaVersion[0:1] + "." + "" + schemaVersion[1:]
}
func extractName(workflow *cncfmodel.Workflow) string {
if len(workflow.ID) > 0 {
return sanitizeNaming(workflow.ID)
}
if len(workflow.Key) > 0 {
return sanitizeNaming(workflow.Key)
}
if len(workflow.Name) > 0 {
return sanitizeNaming(workflow.Name)
}
return ""
}
func sanitizeNaming(name string) string {
if len(name) == 0 || namingRegexp.MatchString(name) {
return name
}
sanitized := startingDashRegexp.ReplaceAllString(allowedCharsRegexp.ReplaceAllString(strings.TrimSpace(strings.ToLower(name)), dash), "")
if len(sanitized) > charLimit {
return sanitized[:charLimit]
}
return sanitized
}