2022-07-07 20:11:50 +00:00
|
|
|
// Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
|
|
|
// Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
|
|
|
// Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are met:
|
|
|
|
//
|
|
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer.
|
|
|
|
//
|
|
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
|
|
// and/or other materials provided with the distribution.
|
|
|
|
//
|
|
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors
|
|
|
|
// may be used to endorse or promote products derived from this software without
|
|
|
|
// specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
package workflow
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"go.temporal.io/sdk/temporal"
|
|
|
|
"go.temporal.io/sdk/workflow"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
|
|
"google.golang.org/protobuf/types/known/anypb"
|
|
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
|
|
"path/filepath"
|
2022-10-30 01:59:25 +00:00
|
|
|
"peridot.resf.org/apollo/rpmutils"
|
2022-07-07 20:11:50 +00:00
|
|
|
"peridot.resf.org/peridot/db/models"
|
|
|
|
peridotpb "peridot.resf.org/peridot/pb"
|
|
|
|
yumrepofspb "peridot.resf.org/peridot/yumrepofs/pb"
|
|
|
|
"peridot.resf.org/utils"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FutureContext struct {
|
|
|
|
Future workflow.Future
|
|
|
|
Ctx workflow.Context
|
|
|
|
TaskQueue string
|
|
|
|
Task *models.Task
|
|
|
|
}
|
|
|
|
|
|
|
|
type sideEffectBuild struct {
|
|
|
|
Task *models.Task
|
|
|
|
Build *models.Build
|
|
|
|
}
|
|
|
|
|
|
|
|
type channelArchBuild struct {
|
|
|
|
err error
|
|
|
|
newArtifacts []*peridotpb.TaskArtifact
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSrpmBuildArches(project *models.Project, metadata *anypb.Any) ([]string, error) {
|
|
|
|
// add all project arches first
|
|
|
|
arches := project.Archs
|
|
|
|
|
|
|
|
// let's check srpm for incompatible arches / noarch
|
|
|
|
rpmArtifactMetadata := &peridotpb.RpmArtifactMetadata{}
|
|
|
|
err := metadata.UnmarshalTo(rpmArtifactMetadata)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(rpmArtifactMetadata.BuildArch) > 0 {
|
|
|
|
arches = rpmArtifactMetadata.BuildArch
|
|
|
|
}
|
|
|
|
// if exclusive to specific arches, then only build for those
|
|
|
|
if len(rpmArtifactMetadata.ExclusiveArch) > 0 {
|
|
|
|
var newArches []string
|
|
|
|
for _, arch := range arches {
|
|
|
|
if utils.StrContains(arch, rpmArtifactMetadata.ExclusiveArch) {
|
|
|
|
newArches = append(newArches, arch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arches = newArches
|
|
|
|
}
|
|
|
|
if len(rpmArtifactMetadata.ExcludeArch) > 0 {
|
|
|
|
var newArches []string
|
|
|
|
for _, arch := range arches {
|
|
|
|
if !utils.StrContains(arch, rpmArtifactMetadata.ExcludeArch) {
|
|
|
|
newArches = append(newArches, arch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arches = newArches
|
|
|
|
}
|
|
|
|
// if noarch isn't excluded and is in build arches or exclusive arches, then add it
|
|
|
|
// the kernel package for example has a separate build process for noarch
|
|
|
|
if !utils.StrContains("noarch", rpmArtifactMetadata.ExcludeArch) && (utils.StrContains("noarch", rpmArtifactMetadata.BuildArch) || utils.StrContains("noarch", rpmArtifactMetadata.ExclusiveArch)) {
|
|
|
|
arches = append(arches, "noarch")
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify that we're not adding any arches that are not in the project
|
|
|
|
var finalArches []string
|
|
|
|
for _, arch := range arches {
|
|
|
|
if arch == "noarch" || utils.StrContains(arch, project.Archs) {
|
|
|
|
// double check that we're not adding duplicate arches
|
|
|
|
if !utils.StrContains(arch, finalArches) {
|
|
|
|
finalArches = append(finalArches, arch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(finalArches) == 0 {
|
|
|
|
return nil, errors.New("no arches found for project")
|
|
|
|
}
|
|
|
|
arches = finalArches
|
|
|
|
|
|
|
|
return arches, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// BuildBatchWorkflow is a workflow that builds a batch of packages (executes BuildWorkflow as a child workflow)
|
|
|
|
func (c *Controller) BuildBatchWorkflow(ctx workflow.Context, req *peridotpb.SubmitBuildBatchRequest, newBuildReqs []string, newModuleBuildReqs []string, buildBatchId string, user *utils.ContextUser) error {
|
|
|
|
var futures []FutureContext
|
|
|
|
|
|
|
|
for _, buildPkg := range newBuildReqs {
|
|
|
|
buildReq := &peridotpb.SubmitBuildRequest{
|
|
|
|
ProjectId: req.ProjectId,
|
|
|
|
Package: &peridotpb.SubmitBuildRequest_PackageName{
|
|
|
|
PackageName: wrapperspb.String(buildPkg),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
triggerCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
|
|
|
|
TaskQueue: c.mainQueue,
|
|
|
|
WorkflowTaskTimeout: 3 * time.Hour,
|
|
|
|
})
|
|
|
|
futures = append(futures, FutureContext{
|
|
|
|
Ctx: triggerCtx,
|
|
|
|
Future: workflow.ExecuteChildWorkflow(triggerCtx, c.TriggerBuildFromBatchWorkflow, buildReq, buildBatchId, false, user),
|
|
|
|
TaskQueue: c.mainQueue,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
for _, buildPkg := range newModuleBuildReqs {
|
|
|
|
buildReq := &peridotpb.SubmitBuildRequest{
|
|
|
|
ProjectId: req.ProjectId,
|
|
|
|
Package: &peridotpb.SubmitBuildRequest_PackageName{
|
|
|
|
PackageName: wrapperspb.String(buildPkg),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
triggerCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
|
|
|
|
TaskQueue: c.mainQueue,
|
|
|
|
WorkflowTaskTimeout: 3 * time.Hour,
|
|
|
|
})
|
|
|
|
futures = append(futures, FutureContext{
|
|
|
|
Ctx: triggerCtx,
|
|
|
|
Future: workflow.ExecuteChildWorkflow(triggerCtx, c.TriggerBuildFromBatchWorkflow, buildReq, buildBatchId, true, user),
|
|
|
|
TaskQueue: c.mainQueue,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build failures doesn't mean a batch trigger has failed
|
|
|
|
// A batch can contain failed builds
|
|
|
|
for _, future := range futures {
|
|
|
|
_ = future.Future.Get(future.Ctx, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TriggerBuildFromBatchWorkflow is a sub-workflow to create a task and trigger a build
|
|
|
|
func (c *Controller) TriggerBuildFromBatchWorkflow(ctx workflow.Context, req *peridotpb.SubmitBuildRequest, buildBatchId string, module bool, user *utils.ContextUser) error {
|
|
|
|
var sideEffect sideEffectBuild
|
|
|
|
sideEffectCall := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
|
|
|
|
projects, err := c.db.ListProjects(&peridotpb.ProjectFilters{
|
|
|
|
Id: wrapperspb.String(req.ProjectId),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(projects) != 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
project := projects[0]
|
|
|
|
|
|
|
|
filters := &peridotpb.PackageFilters{}
|
|
|
|
switch p := req.Package.(type) {
|
|
|
|
case *peridotpb.SubmitBuildRequest_PackageId:
|
|
|
|
filters.Id = p.PackageId
|
|
|
|
case *peridotpb.SubmitBuildRequest_PackageName:
|
|
|
|
filters.NameExact = p.PackageName
|
|
|
|
}
|
|
|
|
|
|
|
|
pkgs, err := c.db.GetPackagesInProject(filters, req.ProjectId, 0, 1)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if len(pkgs) != 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
pkg := pkgs[0]
|
|
|
|
|
|
|
|
var importRevision *models.ImportRevision
|
|
|
|
|
|
|
|
if req.ScmHash == nil {
|
|
|
|
// Each import can have multiple revisions
|
|
|
|
importRevisions, err := c.db.GetLatestImportRevisionsForPackageInProject(pkg.Name, req.ProjectId)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the branch this project is targeting
|
|
|
|
// Using the constructed branch, we will check if there is a revision for the branch
|
|
|
|
// In fork mode, if the project is configured correctly the upstream will have a revision for the branch
|
|
|
|
upstreamBranch := fmt.Sprintf("%s%d%s", project.TargetBranchPrefix, project.MajorVersion, project.BranchSuffix.String)
|
|
|
|
for _, revision := range importRevisions {
|
|
|
|
if revision.ScmBranchName == upstreamBranch {
|
|
|
|
importRevision = &*&revision
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
importRevision, err = c.db.GetImportRevisionByScmHash(req.ScmHash.Value)
|
|
|
|
if err != nil {
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
activityErr := fmt.Errorf("import revision not found for scm hash %s", req.ScmHash.Value)
|
|
|
|
c.log.Errorf("%s", activityErr.Error())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if importRevision == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
beginTx, err := c.db.Begin()
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not start transaction: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
tx := c.db.UseTransaction(beginTx)
|
|
|
|
|
|
|
|
task, err := tx.CreateTask(user, "noarch", peridotpb.TaskType_TASK_TYPE_BUILD, &req.ProjectId, nil)
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not create build task in TriggerBuildFromBatchWorkflow: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
metadataAnyPb, err := anypb.New(&peridotpb.PackageOperationMetadata{
|
|
|
|
PackageName: pkg.Name,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
err = tx.SetTaskMetadata(task.ID.String(), metadataAnyPb)
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not set task metadata in TriggerBuildFromBatchWorkflow: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var build *models.Build
|
|
|
|
if module {
|
|
|
|
err = beginTx.Commit()
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not commit transaction: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
build, err = tx.CreateBuild(pkg.ID.String(), importRevision.PackageVersionId, task.ID.String(), req.ProjectId)
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not create build in TriggerBuildFromBatchWorkflow: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = tx.AttachBuildToBatch(build.ID.String(), buildBatchId)
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not attach build to batch: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = beginTx.Commit()
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("could not commit transaction: %v", err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &sideEffectBuild{
|
|
|
|
Task: task,
|
|
|
|
Build: build,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
err := sideEffectCall.Get(&sideEffect)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if sideEffect.Task == nil {
|
|
|
|
return fmt.Errorf("could not trigger build")
|
|
|
|
}
|
|
|
|
|
|
|
|
buildCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
|
|
|
|
WorkflowID: sideEffect.Task.ID.String(),
|
|
|
|
TaskQueue: c.mainQueue,
|
|
|
|
WorkflowTaskTimeout: 3 * time.Hour,
|
|
|
|
RetryPolicy: &temporal.RetryPolicy{
|
|
|
|
MaximumAttempts: 1,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
if module {
|
|
|
|
return workflow.ExecuteChildWorkflow(buildCtx, c.BuildModuleWorkflow, req, sideEffect.Task, &peridotpb.ExtraBuildOptions{
|
|
|
|
BuildBatchId: buildBatchId,
|
|
|
|
}).Get(buildCtx, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
return workflow.ExecuteChildWorkflow(buildCtx, c.BuildWorkflow, req, sideEffect.Task, &peridotpb.ExtraBuildOptions{
|
|
|
|
BuildBatchId: buildBatchId,
|
|
|
|
ReusableBuildId: sideEffect.Build.ID.String(),
|
|
|
|
}).Get(buildCtx, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
// BuildWorkflow initiates a new artifact build, which can currently be a standard package and a module.
|
|
|
|
// Modules trigger build activities for each component
|
|
|
|
// todo(mustafa): More info
|
|
|
|
func (c *Controller) BuildWorkflow(ctx workflow.Context, req *peridotpb.SubmitBuildRequest, task *models.Task, extraOptions *peridotpb.ExtraBuildOptions) (*peridotpb.SubmitBuildTask, error) {
|
|
|
|
submitBuildTask := peridotpb.SubmitBuildTask{
|
|
|
|
ChecksDisabled: req.DisableChecks,
|
|
|
|
ParentTaskId: utils.NullStringValueP(task.ParentTaskId),
|
|
|
|
}
|
|
|
|
|
|
|
|
deferTask, errorDetails, err := c.commonCreateTask(task, nil)
|
|
|
|
defer deferTask()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
filters := &peridotpb.PackageFilters{}
|
|
|
|
switch p := req.Package.(type) {
|
|
|
|
case *peridotpb.SubmitBuildRequest_PackageId:
|
|
|
|
filters.Id = p.PackageId
|
|
|
|
case *peridotpb.SubmitBuildRequest_PackageName:
|
|
|
|
filters.NameExact = p.PackageName
|
|
|
|
}
|
|
|
|
|
|
|
|
pkgs, err := c.db.GetPackagesInProject(filters, req.ProjectId, 0, 1)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(pkgs) != 1 {
|
|
|
|
setPackageNotFoundError(errorDetails, req.ProjectId, ErrorDomainBuildsPeridot)
|
|
|
|
return nil, utils.CouldNotRetrieveObjects
|
|
|
|
}
|
|
|
|
pkg := pkgs[0]
|
|
|
|
|
|
|
|
projects, err := c.db.ListProjects(&peridotpb.ProjectFilters{
|
|
|
|
Id: wrapperspb.String(req.ProjectId),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(projects) != 1 {
|
|
|
|
setInternalError(errorDetails, errors.New("project could not be found"))
|
|
|
|
return nil, utils.CouldNotRetrieveObjects
|
|
|
|
}
|
|
|
|
project := projects[0]
|
|
|
|
|
|
|
|
var importRevision *models.ImportRevision
|
|
|
|
|
|
|
|
if req.ScmHash == nil {
|
|
|
|
// Each import can have multiple revisions
|
|
|
|
importRevisions, err := c.db.GetLatestImportRevisionsForPackageInProject(pkg.Name, req.ProjectId)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the branch this project is targeting
|
|
|
|
// Using the constructed branch, we will check if there is a revision for the branch
|
|
|
|
// In fork mode, if the project is configured correctly the upstream will have a revision for the branch
|
|
|
|
upstreamBranch := fmt.Sprintf("%s%d%s", project.TargetBranchPrefix, project.MajorVersion, project.BranchSuffix.String)
|
|
|
|
for _, revision := range importRevisions {
|
|
|
|
if revision.ScmBranchName == upstreamBranch {
|
|
|
|
importRevision = &*&revision
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
importRevision, err = c.db.GetImportRevisionByScmHash(req.ScmHash.Value)
|
|
|
|
if err != nil {
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
activityErr := fmt.Errorf("import revision not found for scm hash %s", req.ScmHash.Value)
|
|
|
|
setActivityError(errorDetails, activityErr)
|
|
|
|
return nil, activityErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if importRevision == nil {
|
|
|
|
setActivityError(errorDetails, errors.New("could not find upstream branch"))
|
|
|
|
return nil, errors.New("could not find upstream branch")
|
|
|
|
}
|
|
|
|
packageVersion, err := c.db.GetPackageVersion(importRevision.PackageVersionId)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("could not get package version for import revision %s: %v", importRevision.ID, err)
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
// If there is a parent task, we need to use that ID as the parent task ID
|
|
|
|
taskID := task.ID.String()
|
|
|
|
if task.ParentTaskId.Valid {
|
|
|
|
taskID = task.ParentTaskId.String
|
|
|
|
}
|
|
|
|
|
2022-07-07 20:11:50 +00:00
|
|
|
// Create a side repo if the build request specifies side NVRs
|
|
|
|
// Packages specified here will be excluded from the main repo
|
|
|
|
if len(req.SideNvrs) > 0 {
|
2022-11-19 01:52:24 +00:00
|
|
|
var buildNvrs []models.Build
|
2022-07-07 20:11:50 +00:00
|
|
|
var excludes []string
|
|
|
|
for _, sideNvr := range req.SideNvrs {
|
|
|
|
if !rpmutils.NVRNoArch().MatchString(sideNvr) {
|
|
|
|
err = fmt.Errorf("invalid side NVR: %s", sideNvr)
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
nvrMatch := rpmutils.NVRNoArch().FindStringSubmatch(sideNvr)
|
|
|
|
nvrName := nvrMatch[1]
|
|
|
|
nvrVersion := nvrMatch[2]
|
|
|
|
nvrRelease := nvrMatch[3]
|
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
buildNvrsFromBuild, err := c.db.GetBuildByPackageNameAndVersionAndRelease(nvrName, nvrVersion, nvrRelease)
|
2022-07-07 20:11:50 +00:00
|
|
|
if err != nil {
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
err = fmt.Errorf("side NVR %s not found in project %s", sideNvr, req.ProjectId)
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = fmt.Errorf("could not get build for side NVR %s: %v", sideNvr, err)
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
for _, buildNvr := range buildNvrsFromBuild {
|
|
|
|
artifacts, err := c.db.GetArtifactsForBuild(buildNvr.ID.String())
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("could not get artifacts for build %s: %v", buildNvr.ID, err)
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
for _, artifact := range artifacts {
|
|
|
|
artifactName := strings.TrimPrefix(artifact.Name, fmt.Sprintf("%s/", buildNvr.TaskId))
|
|
|
|
if rpmutils.NVR().MatchString(artifactName) {
|
|
|
|
excludes = append(excludes, rpmutils.NVR().FindStringSubmatch(artifactName)[1])
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
buildNvrs = append(buildNvrs, buildNvrsFromBuild...)
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
repo, err := c.db.CreateRepositoryWithPackages(uuid.New().String(), req.ProjectId, true, []string{})
|
|
|
|
if err != nil {
|
|
|
|
c.log.Errorf("failed to create repository: %v", err)
|
|
|
|
return nil, status.Error(codes.Internal, "failed to create repository for side nvrs")
|
|
|
|
}
|
|
|
|
|
|
|
|
if extraOptions == nil {
|
|
|
|
extraOptions = &peridotpb.ExtraBuildOptions{}
|
|
|
|
}
|
|
|
|
if extraOptions.ExtraYumrepofsRepos == nil {
|
|
|
|
extraOptions.ExtraYumrepofsRepos = []*peridotpb.ExtraYumrepofsRepo{}
|
|
|
|
}
|
2022-11-19 20:08:54 +00:00
|
|
|
if extraOptions.ExcludePackages == nil {
|
|
|
|
extraOptions.ExcludePackages = []string{}
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
extraOptions.ExtraYumrepofsRepos = append(extraOptions.ExtraYumrepofsRepos, &peridotpb.ExtraYumrepofsRepo{
|
|
|
|
Name: repo.Name,
|
|
|
|
ModuleHotfixes: true,
|
|
|
|
IgnoreExclude: true,
|
|
|
|
})
|
2022-11-19 20:08:54 +00:00
|
|
|
extraOptions.ExcludePackages = append(extraOptions.ExcludePackages, excludes...)
|
2022-07-07 20:11:50 +00:00
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
var buildIds []string
|
|
|
|
for _, build := range buildNvrs {
|
|
|
|
buildIds = append(buildIds, build.ID.String())
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
|
2022-11-19 01:52:24 +00:00
|
|
|
yumrepoCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
|
|
|
|
TaskQueue: "yumrepofs",
|
|
|
|
})
|
|
|
|
updateRepoRequest := &UpdateRepoRequest{
|
|
|
|
ProjectID: req.ProjectId,
|
|
|
|
TaskID: &taskID,
|
|
|
|
BuildIDs: buildIds,
|
|
|
|
Delete: false,
|
|
|
|
ForceRepoId: repo.ID.String(),
|
|
|
|
ForceNonModular: true,
|
|
|
|
DisableSigning: true,
|
|
|
|
DisableSetActive: true,
|
|
|
|
}
|
|
|
|
updateRepoTask := &yumrepofspb.UpdateRepoTask{}
|
|
|
|
err = workflow.ExecuteChildWorkflow(yumrepoCtx, c.RepoUpdaterWorkflow, updateRepoRequest).Get(ctx, updateRepoTask)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
2022-11-19 01:52:24 +00:00
|
|
|
|
2022-07-07 20:11:50 +00:00
|
|
|
buildID := extraOptions.ReusableBuildId
|
|
|
|
if buildID == "" {
|
|
|
|
err = errors.New("reusable build id not found")
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
projectId := project.ID.String()
|
|
|
|
var srpmTask models.Task
|
|
|
|
srpmTaskEffect := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
|
|
|
|
newTask, err := c.db.CreateTask(nil, "noarch", peridotpb.TaskType_TASK_TYPE_BUILD_SRPM, &projectId, &taskID)
|
|
|
|
if err != nil {
|
|
|
|
return &models.Task{}
|
|
|
|
}
|
|
|
|
return newTask
|
|
|
|
})
|
|
|
|
err = srpmTaskEffect.Get(&srpmTask)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, fmt.Errorf("could not get srpm task effect: %v", err))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !srpmTask.ProjectId.Valid {
|
|
|
|
setInternalError(errorDetails, fmt.Errorf("could not create srpm task"))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Provision a new worker specifically to build the SRPM
|
|
|
|
// SRPMs can be built with any architecture
|
|
|
|
srpmTaskQueue, cleanupSrpm, err := c.provisionWorker(ctx, &ProvisionWorkerRequest{
|
|
|
|
TaskId: srpmTask.ID.String(),
|
|
|
|
ParentTaskId: sql.NullString{String: taskID, Valid: true},
|
|
|
|
Purpose: "srpm",
|
|
|
|
Arch: "noarch",
|
|
|
|
ProjectId: req.ProjectId,
|
|
|
|
Privileged: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer cleanupSrpm()
|
|
|
|
|
|
|
|
srpmCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
|
|
|
ScheduleToStartTimeout: 30 * time.Minute,
|
|
|
|
StartToCloseTimeout: time.Hour,
|
|
|
|
HeartbeatTimeout: 2 * time.Minute,
|
|
|
|
TaskQueue: srpmTaskQueue,
|
|
|
|
RetryPolicy: &temporal.RetryPolicy{
|
|
|
|
MaximumAttempts: 1,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
upstreamPrefix := fmt.Sprintf("%s/%s", project.TargetGitlabHost, project.TargetPrefix)
|
|
|
|
err = workflow.ExecuteActivity(srpmCtx, c.BuildSRPMActivity, upstreamPrefix, importRevision.ScmHash, project.ID.String(), pkg.Name, packageVersion, srpmTask, extraOptions).Get(srpmCtx, nil)
|
|
|
|
if err != nil {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
uploadSrpmCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
|
|
|
ScheduleToStartTimeout: 30 * time.Minute,
|
|
|
|
StartToCloseTimeout: time.Hour,
|
|
|
|
HeartbeatTimeout: time.Minute,
|
|
|
|
TaskQueue: srpmTaskQueue,
|
|
|
|
RetryPolicy: &temporal.RetryPolicy{
|
|
|
|
MaximumAttempts: 3,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
var uploadSRPMResult UploadActivityResult
|
|
|
|
err = workflow.ExecuteActivity(uploadSrpmCtx, c.UploadSRPMActivity, project.ID.String(), taskID).Get(uploadSrpmCtx, &uploadSRPMResult)
|
|
|
|
if err != nil {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cleanupSrpm()
|
|
|
|
|
|
|
|
// todo(mustafa): Currently we do not support partial arch builds
|
|
|
|
// Partial arch builds are when you introduce new architectures to a project
|
|
|
|
// and be able to build them without affecting the existing architectures.
|
|
|
|
// That is an important feature for Peridot, but not strictly necessary as of now.
|
|
|
|
nvr := strings.TrimSuffix(filepath.Base(uploadSRPMResult.ObjectName), ".rpm")
|
|
|
|
|
|
|
|
firstNvrCheckSideEffectCall := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
|
|
|
|
exists, err := c.db.NVRAExists(nvr)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("could not check if NVR exists: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return exists
|
|
|
|
})
|
|
|
|
var res interface{}
|
|
|
|
err = firstNvrCheckSideEffectCall.Get(&res)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err, ok := res.(error); ok {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if exists, ok := res.(bool); ok && exists {
|
|
|
|
err = fmt.Errorf("NVR %s already locked", nvr)
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_CANCELED
|
|
|
|
_ = c.logToMon([]string{err.Error()}, task.ID.String(), taskID)
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-08-24 21:25:25 +00:00
|
|
|
subtask, err := c.db.GetTask(uploadSRPMResult.Subtask.ID.String(), utils.Pointer(project.ID.String()))
|
2022-07-07 20:11:50 +00:00
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metadata := &anypb.Any{}
|
|
|
|
err = protojson.Unmarshal(subtask[0].Metadata.JSONText, metadata)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
arches, err := getSrpmBuildArches(&project, metadata)
|
|
|
|
if err != nil {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke build for all archs
|
|
|
|
var artifacts []*peridotpb.TaskArtifact
|
|
|
|
archChannel := workflow.NewChannel(ctx)
|
|
|
|
for _, archTop := range arches {
|
|
|
|
archGoCtx := workflow.WithValue(ctx, "arch", archTop)
|
|
|
|
workflow.Go(archGoCtx, func(ctx workflow.Context) {
|
|
|
|
arch := ctx.Value("arch").(string)
|
|
|
|
ret := &channelArchBuild{}
|
|
|
|
defer archChannel.Send(ctx, ret)
|
|
|
|
|
|
|
|
var archTask models.Task
|
|
|
|
archTaskEffect := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
|
|
|
|
newTask, err := c.db.CreateTask(nil, arch, peridotpb.TaskType_TASK_TYPE_BUILD_ARCH, &projectId, &taskID)
|
|
|
|
if err != nil {
|
|
|
|
return &models.Task{}
|
|
|
|
}
|
|
|
|
return newTask
|
|
|
|
})
|
|
|
|
err := archTaskEffect.Get(&archTask)
|
|
|
|
if err != nil || !archTask.ProjectId.Valid {
|
|
|
|
ret.err = fmt.Errorf("failed to create arch task: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
workerReq := &ProvisionWorkerRequest{
|
|
|
|
TaskId: archTask.ID.String(),
|
|
|
|
ParentTaskId: sql.NullString{String: taskID, Valid: true},
|
|
|
|
Purpose: "b-" + arch,
|
|
|
|
Arch: arch,
|
|
|
|
ProjectId: req.ProjectId,
|
|
|
|
HighResource: true,
|
|
|
|
Privileged: true,
|
|
|
|
}
|
|
|
|
archTaskQueue, cleanupArch, err := c.provisionWorker(ctx, workerReq)
|
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to provision worker: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer cleanupArch()
|
|
|
|
|
|
|
|
archCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
|
|
|
ScheduleToStartTimeout: 12 * time.Hour,
|
|
|
|
StartToCloseTimeout: 48 * time.Hour,
|
|
|
|
HeartbeatTimeout: 2 * time.Hour,
|
|
|
|
TaskQueue: archTaskQueue,
|
|
|
|
RetryPolicy: &temporal.RetryPolicy{
|
|
|
|
MaximumAttempts: 1,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
err = workflow.ExecuteActivity(archCtx, c.BuildArchActivity, project.ID.String(), pkg.Name, req.DisableChecks, packageVersion, uploadSRPMResult, archTask, arch, extraOptions).Get(archCtx, nil)
|
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to build arch %s: %s", arch, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
uploadArchCtx := workflow.WithActivityOptions(ctx, workflow.ActivityOptions{
|
|
|
|
ScheduleToStartTimeout: 12 * time.Hour,
|
|
|
|
StartToCloseTimeout: 24 * time.Hour,
|
|
|
|
HeartbeatTimeout: 2 * time.Minute,
|
|
|
|
TaskQueue: archTaskQueue,
|
|
|
|
})
|
|
|
|
|
|
|
|
var res []*UploadActivityResult
|
|
|
|
err = workflow.ExecuteActivity(uploadArchCtx, c.UploadArchActivity, project.ID.String(), taskID).Get(ctx, &res)
|
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to upload arch %s: %s", arch, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
exists, err := c.db.NVRAExists(strings.TrimSuffix(filepath.Base(uploadSRPMResult.ObjectName), ".rpm"))
|
|
|
|
if err != nil {
|
|
|
|
ret.err = err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only refresh SRPM if we don't already have it in the repo tree
|
|
|
|
// This is to enable us to rebuild archs we don't have while keeping
|
|
|
|
// the integrity of the already published repository
|
|
|
|
if !exists {
|
|
|
|
res = append(res, &uploadSRPMResult)
|
|
|
|
}
|
|
|
|
|
|
|
|
var newArtifacts []*peridotpb.TaskArtifact
|
|
|
|
for _, result := range res {
|
|
|
|
err = c.db.AttachTaskToBuild(buildID, result.Subtask.ID.String())
|
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to attach task to build: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if result.Skip {
|
|
|
|
continue
|
|
|
|
}
|
2022-08-24 21:25:25 +00:00
|
|
|
subtask, err := c.db.GetTask(result.Subtask.ID.String(), utils.Pointer(project.ID.String()))
|
2022-07-07 20:11:50 +00:00
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to get task: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
metadata := &anypb.Any{}
|
|
|
|
err = protojson.Unmarshal(subtask[0].Metadata.JSONText, metadata)
|
|
|
|
if err != nil {
|
|
|
|
ret.err = fmt.Errorf("failed to unmarshal metadata: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ret.newArtifacts = append(newArtifacts, &peridotpb.TaskArtifact{
|
|
|
|
TaskId: result.Subtask.ID.String(),
|
|
|
|
Name: result.ObjectName,
|
|
|
|
HashSha256: result.HashSha256,
|
|
|
|
Arch: result.Arch,
|
|
|
|
Metadata: metadata,
|
|
|
|
})
|
2022-11-14 10:06:16 +00:00
|
|
|
artifacts = append(artifacts, &peridotpb.TaskArtifact{
|
|
|
|
TaskId: result.Subtask.ID.String(),
|
|
|
|
Name: result.ObjectName,
|
|
|
|
HashSha256: result.HashSha256,
|
|
|
|
Arch: result.Arch,
|
|
|
|
})
|
2022-07-07 20:11:50 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var artifactNames []string
|
|
|
|
archWaitChannel := workflow.NewChannel(ctx)
|
|
|
|
for i := 0; i < len(arches); i++ {
|
|
|
|
workflow.Go(ctx, func(ctx workflow.Context) {
|
|
|
|
var res channelArchBuild
|
|
|
|
archChannel.Receive(ctx, &res)
|
|
|
|
|
|
|
|
if res.err != nil {
|
|
|
|
archWaitChannel.Send(ctx, res.err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, artifact := range res.newArtifacts {
|
|
|
|
if utils.StrContains(artifact.Name, artifactNames) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
artifactNames = append(artifactNames, artifact.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
archWaitChannel.Send(ctx, nil)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var archErrs []error
|
|
|
|
for i := 0; i < len(arches); i++ {
|
|
|
|
var res interface{}
|
|
|
|
archWaitChannel.Receive(ctx, &res)
|
|
|
|
if err, ok := res.(error); ok {
|
|
|
|
if err != nil {
|
|
|
|
archErrs = append(archErrs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isError := false
|
|
|
|
isCanceled := false
|
|
|
|
var retErr error
|
|
|
|
for _, archErr := range archErrs {
|
|
|
|
if archErr != nil {
|
|
|
|
if !strings.Contains(archErr.Error(), "canceled") {
|
|
|
|
isError = true
|
|
|
|
retErr = archErr
|
|
|
|
}
|
|
|
|
if !isError && strings.Contains(archErr.Error(), "canceled") {
|
|
|
|
isCanceled = true
|
|
|
|
retErr = archErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if retErr != nil {
|
|
|
|
setActivityError(errorDetails, retErr)
|
|
|
|
if isCanceled {
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_CANCELED
|
|
|
|
} else {
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_FAILED
|
|
|
|
}
|
|
|
|
_ = c.db.SetTaskStatus(task.ID.String(), task.Status)
|
|
|
|
|
|
|
|
return nil, retErr
|
|
|
|
}
|
|
|
|
|
|
|
|
submitBuildTask = peridotpb.SubmitBuildTask{
|
|
|
|
BuildId: buildID,
|
2022-11-14 10:06:16 +00:00
|
|
|
BuildTaskId: task.ID.String(),
|
2022-07-07 20:11:50 +00:00
|
|
|
PackageName: pkg.Name,
|
|
|
|
ImportRevision: importRevision.ToProto(),
|
|
|
|
Artifacts: artifacts,
|
|
|
|
ChecksDisabled: req.DisableChecks,
|
|
|
|
Modular: req.ModuleVariant,
|
|
|
|
ParentTaskId: utils.NullStringValueP(task.ParentTaskId),
|
|
|
|
RepoChanges: &yumrepofspb.UpdateRepoTask{Changes: []*yumrepofspb.RepositoryChange{}},
|
|
|
|
}
|
|
|
|
sbtAny, err := anypb.New(&submitBuildTask)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = c.db.SetTaskResponse(task.ID.String(), sbtAny)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_RUNNING
|
|
|
|
|
|
|
|
// Save once here so RepoUpdaterWorkflow can use it
|
|
|
|
deferTask()
|
|
|
|
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_FAILED
|
|
|
|
|
|
|
|
if !extraOptions.DisableYumrepofsUpdates && !req.SetInactive {
|
|
|
|
yumrepoCtx := workflow.WithChildOptions(ctx, workflow.ChildWorkflowOptions{
|
|
|
|
TaskQueue: "yumrepofs",
|
|
|
|
})
|
|
|
|
updateRepoRequest := &UpdateRepoRequest{
|
|
|
|
ProjectID: req.ProjectId,
|
|
|
|
BuildIDs: []string{buildID},
|
|
|
|
Delete: false,
|
|
|
|
TaskID: &taskID,
|
|
|
|
}
|
|
|
|
updateRepoTask := &yumrepofspb.UpdateRepoTask{}
|
|
|
|
err = workflow.ExecuteChildWorkflow(yumrepoCtx, c.RepoUpdaterWorkflow, updateRepoRequest).Get(yumrepoCtx, updateRepoTask)
|
|
|
|
if err != nil {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
submitBuildTask.RepoChanges = updateRepoTask
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lock NVR only once
|
|
|
|
effectCallNVRA := workflow.SideEffect(ctx, func(ctx workflow.Context) interface{} {
|
|
|
|
err = c.db.LockNVRA(nvr)
|
|
|
|
if err != nil {
|
|
|
|
// retry once again, stupid error
|
|
|
|
err = c.db.LockNVRA(nvr)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("failed to lock nvr: %s", err)
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
var resNVRA interface{}
|
|
|
|
err = effectCallNVRA.Get(&resNVRA)
|
|
|
|
if err != nil {
|
|
|
|
setInternalError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err, ok := resNVRA.(error); ok {
|
|
|
|
setActivityError(errorDetails, err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
task.Status = peridotpb.TaskStatus_TASK_STATUS_SUCCEEDED
|
|
|
|
|
|
|
|
return &submitBuildTask, nil
|
|
|
|
}
|