blob: d50b599937ce140c81121c15739aab320a95db7b [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 builder
import (
"time"
"k8s.io/klog/v2"
"github.com/kiegroup/kogito-serverless-operator/controllers/workflowdef"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
operatorapi "github.com/kiegroup/kogito-serverless-operator/api/v1alpha08"
clientr "github.com/kiegroup/kogito-serverless-operator/container-builder/client"
"github.com/kiegroup/kogito-serverless-operator/controllers/platform"
"github.com/kiegroup/kogito-serverless-operator/utils"
"github.com/kiegroup/kogito-serverless-operator/container-builder/api"
builder "github.com/kiegroup/kogito-serverless-operator/container-builder/builder/kubernetes"
"github.com/kiegroup/kogito-serverless-operator/container-builder/client"
"github.com/kiegroup/kogito-serverless-operator/log"
)
const (
resourceDockerfile = "Dockerfile"
)
var _ BuildManager = &containerBuilderManager{}
type kanikoBuildInput struct {
name string
task *api.KanikoTask
workflowDefinition []byte
workflow *operatorapi.SonataFlow
dockerfile string
imageTag string
}
type containerBuilderManager struct {
buildManagerContext
// needed for the internal container-builder
restConfig *rest.Config
}
func (c *containerBuilderManager) Schedule(build *operatorapi.SonataFlowBuild) error {
kanikoTaskCache := api.KanikoTaskCache{}
if platform.IsKanikoCacheEnabled(c.platform) {
kanikoTaskCache.Enabled = utils.Pbool(true)
}
kanikoTask := &api.KanikoTask{
ContainerBuildBaseTask: api.ContainerBuildBaseTask{
Name: "kaniko",
BuildArgs: build.Spec.BuildArgs,
Envs: build.Spec.Envs,
Resources: build.Spec.Resources,
},
PublishTask: api.PublishTask{},
Cache: kanikoTaskCache,
AdditionalFlags: build.Spec.Arguments,
}
var containerBuilder *api.ContainerBuild
var err error
if containerBuilder, err = c.scheduleNewKanikoBuildWithContainerFile(build, kanikoTask); err != nil {
return err
}
if containerBuilder == nil {
return nil
}
if err = build.Status.SetInnerBuild(containerBuilder); err != nil {
return err
}
build.Status.BuildPhase = operatorapi.BuildPhase(containerBuilder.Status.Phase)
build.Status.Error = containerBuilder.Status.Error
return nil
}
func (c *containerBuilderManager) Reconcile(build *operatorapi.SonataFlowBuild) error {
containerBuild := &api.ContainerBuild{}
if err := build.Status.GetInnerBuild(containerBuild); err != nil {
return err
}
containerCli, _ := clientr.FromCtrlClientSchemeAndConfig(c.client, c.client.Scheme(), c.restConfig)
containerBuild, err := c.reconcileBuild(containerBuild, containerCli)
if err != nil {
return err
}
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
}
return nil
}
func newContainerBuilderManager(managerContext buildManagerContext, config *rest.Config) BuildManager {
return &containerBuilderManager{
buildManagerContext: managerContext,
restConfig: config,
}
}
func (c *containerBuilderManager) scheduleNewKanikoBuildWithContainerFile(build *operatorapi.SonataFlowBuild, task *api.KanikoTask) (*api.ContainerBuild, error) {
workflow, err := c.fetchWorkflowForBuild(build)
if err != nil {
return nil, err
}
workflowDef, err := workflowdef.GetJSONWorkflow(workflow, c.ctx)
if err != nil {
return nil, err
}
buildInput := kanikoBuildInput{
name: workflow.Name,
task: task,
workflowDefinition: workflowDef,
workflow: workflow,
dockerfile: platform.GetCustomizedDockerfile(c.commonConfig.Data[c.commonConfig.Data[configKeyDefaultBuilderResourceName]], *c.platform),
imageTag: workflowdef.GetWorkflowAppImageNameTag(workflow),
}
if c.platform.Spec.Build.Config.Timeout == nil {
c.platform.Spec.Build.Config.Timeout = &metav1.Duration{Duration: 5 * time.Minute}
}
return c.buildImage(buildInput)
}
func (c *containerBuilderManager) reconcileBuild(build *api.ContainerBuild, cli client.Client) (*api.ContainerBuild, error) {
result, err := builder.FromBuild(build).WithClient(cli).Reconcile()
return result, err
}
func (c *containerBuilderManager) buildImage(buildInput kanikoBuildInput) (*api.ContainerBuild, error) {
cli, err := client.FromCtrlClientSchemeAndConfig(c.client, c.client.Scheme(), c.restConfig)
plat := api.PlatformContainerBuild{
ObjectReference: api.ObjectReference{
Namespace: c.platform.Namespace,
Name: buildInput.name,
},
Spec: api.PlatformContainerBuildSpec{
BuildStrategy: api.ContainerBuildStrategyPod,
PublishStrategy: api.PlatformBuildPublishStrategyKaniko,
Registry: api.ContainerRegistrySpec{
Insecure: c.platform.Spec.Build.Config.Registry.Insecure,
Address: c.platform.Spec.Build.Config.Registry.Address,
Secret: c.platform.Spec.Build.Config.Registry.Secret,
},
Timeout: &metav1.Duration{
Duration: c.platform.Spec.Build.Config.Timeout.Duration,
},
},
}
build, err := newBuild(buildInput, plat, c.commonConfig.Data[configKeyDefaultExtension], cli)
if err != nil {
klog.V(log.E).ErrorS(err, "error during build Image")
return nil, err
}
return build, err
}
// Helper function to create a new container-builder build and schedule it
func newBuild(buildInput kanikoBuildInput, platform api.PlatformContainerBuild, defaultExtension string, cli client.Client) (*api.ContainerBuild, error) {
buildInfo := builder.ContainerBuilderInfo{FinalImageName: buildInput.imageTag, BuildUniqueName: buildInput.name, Platform: platform}
newBuilder := builder.NewBuild(buildInfo).
WithClient(cli).
AddResource(resourceDockerfile, []byte(buildInput.dockerfile)).
AddResource(buildInput.name+defaultExtension, buildInput.workflowDefinition)
for _, res := range buildInput.workflow.Spec.Resources.ConfigMaps {
newBuilder.AddConfigMapResource(res.ConfigMap, res.WorkflowPath)
}
return newBuilder.Scheduler().
WithAdditionalArgs(buildInput.task.AdditionalFlags).
WithResourceRequirements(buildInput.task.Resources).
WithBuildArgs(buildInput.task.BuildArgs).
WithEnvs(buildInput.task.Envs).Schedule()
}