blob: cf7ec9e556729a4ec728edf0b5bdfbbd2a934d3c [file] [log] [blame]
// 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 platform
import (
"context"
"regexp"
"runtime"
"strings"
"time"
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"
"github.com/kiegroup/kogito-serverless-operator/controllers/workflowdef"
"github.com/kiegroup/kogito-serverless-operator/container-builder/client"
"github.com/kiegroup/kogito-serverless-operator/container-builder/util/defaults"
"github.com/kiegroup/kogito-serverless-operator/log"
operatorapi "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08"
)
var builderDockerfileFromRE = regexp.MustCompile(`FROM (.*) AS builder`)
// ResourceCustomizer can be used to inject code that changes the objects before they are created.
type ResourceCustomizer func(object ctrl.Object) ctrl.Object
func ConfigureRegistry(ctx context.Context, c client.Client, p *operatorapi.SonataFlowPlatform, verbose bool) error {
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.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.Build.Config.Registry.Address = *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.Build.Config.BuildStrategyOptions == nil {
klog.V(log.D).InfoS("SonataFlow Platform: setting publish strategy options", "namespace", p.Namespace)
p.Spec.Build.Config.BuildStrategyOptions = map[string]string{}
}
if p.Spec.Build.Config.GetTimeout().Duration != 0 {
d := p.Spec.Build.Config.GetTimeout().Duration.Truncate(time.Second)
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.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.Build.Config.Timeout = &metav1.Duration{
Duration: 5 * time.Minute,
}
}
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.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.Build.Config.BuildStrategyOptions[kanikoBuildCacheEnabled] = defaultKanikoBuildCache
if verbose {
klog.V(log.I).InfoS("Kaniko cache set", "value", defaultKanikoBuildCache)
}
}
setStatusAdditionalInfo(p)
if verbose {
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
}
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.Build.Config.BuildStrategy == operatorapi.OperatorBuildStrategy {
platform.Status.Info["kanikoVersion"] = defaults.KanikoVersion
}
klog.V(log.D).InfoS("SonataFlow setting status info", "namespace", platform.Namespace)
platform.Status.Info["goVersion"] = runtime.Version()
platform.Status.Info["goOS"] = runtime.GOOS
}
// GetRegistryAddress KEP-1755
// https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
func GetRegistryAddress(ctx context.Context, c client.Client) (*string, error) {
config := corev1.ConfigMap{}
err := c.Get(ctx, ctrl.ObjectKey{Namespace: "kube-public", Name: "local-registry-hosting"}, &config)
if err != nil {
if k8serrors.IsNotFound(err) {
return nil, nil
}
return nil, err
}
if data, ok := config.Data["localRegistryHosting.v1"]; ok {
result := LocalRegistryHostingV1{}
if err := yaml.Unmarshal([]byte(data), &result); err != nil {
return nil, err
}
return &result.HostFromClusterNetwork, nil
}
return nil, nil
}
// GetCustomizedDockerfile gets the Dockerfile as defined in the default platform ConfigMap, apply any custom requirements and return.
func GetCustomizedDockerfile(dockerfile string, platform operatorapi.SonataFlowPlatform) string {
if platform.Spec.Build.Config.BaseImage != "" {
res := builderDockerfileFromRE.FindAllStringSubmatch(dockerfile, 1)
dockerfile = strings.Replace(dockerfile, strings.Trim(res[0][1], " "), platform.Spec.Build.Config.BaseImage, 1)
}
return dockerfile
}