diff --git a/pipeline/backend/kubernetes/kubernetes.go b/pipeline/backend/kubernetes/kubernetes.go index b3eab0ba0..b6d5c5d29 100644 --- a/pipeline/backend/kubernetes/kubernetes.go +++ b/pipeline/backend/kubernetes/kubernetes.go @@ -41,6 +41,7 @@ import ( "k8s.io/client-go/tools/cache" "go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types" + pipelineErrors "go.woodpecker-ci.org/woodpecker/v3/pipeline/errors/types" ) const ( @@ -237,7 +238,10 @@ func (e *kube) SetupWorkflow(ctx context.Context, conf *types.Config, taskUUID s if isService(step) { svc, err := startService(ctx, e, step) if err != nil { - return err + return &pipelineErrors.ErrInvalidWorkflowSetup{ + Err: err, + Step: step, + } } hostAlias := types.HostAlias{Name: step.Networks[0].Aliases[0], IP: svc.Spec.ClusterIP} extraHosts = append(extraHosts, hostAlias) diff --git a/pipeline/backend/kubernetes/service.go b/pipeline/backend/kubernetes/service.go index e3a7d57a7..22ee68925 100644 --- a/pipeline/backend/kubernetes/service.go +++ b/pipeline/backend/kubernetes/service.go @@ -43,6 +43,10 @@ func mkService(step *types.Step, config *config) (*v1.Service, error) { ServiceLabel: name, } + if len(step.Ports) == 0 { + return nil, fmt.Errorf("kubernetes backend requires explicitly exposed ports for service steps, add 'ports' configuration to step '%s'", step.Name) + } + var svcPorts []v1.ServicePort for _, port := range step.Ports { svcPorts = append(svcPorts, servicePort(port)) diff --git a/pipeline/errors/types/errors.go b/pipeline/errors/types/errors.go index 752eb6904..caa85c08e 100644 --- a/pipeline/errors/types/errors.go +++ b/pipeline/errors/types/errors.go @@ -1,6 +1,10 @@ package types -import "fmt" +import ( + "fmt" + + backend "go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types" +) type PipelineErrorType string @@ -22,3 +26,15 @@ type PipelineError struct { func (e *PipelineError) Error() string { return fmt.Sprintf("[%s] %s", e.Type, e.Message) } + +type ErrInvalidWorkflowSetup struct { + Err error + Step *backend.Step +} + +func (e *ErrInvalidWorkflowSetup) Error() string { + if e.Step != nil { + return fmt.Sprintf("error in workflow setup step '%s': %v", e.Step.Name, e.Err) + } + return fmt.Sprintf("error in workflow setup: %v", e.Err) +} diff --git a/pipeline/pipeline.go b/pipeline/pipeline.go index c7429dbf3..622917be2 100644 --- a/pipeline/pipeline.go +++ b/pipeline/pipeline.go @@ -27,6 +27,7 @@ import ( "golang.org/x/sync/errgroup" backend "go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types" + pipelineErrors "go.woodpecker-ci.org/woodpecker/v3/pipeline/errors/types" "go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/metadata" ) @@ -116,6 +117,25 @@ func (r *Runtime) Run(runnerCtx context.Context) error { r.started = time.Now().Unix() if err := r.engine.SetupWorkflow(runnerCtx, r.spec, r.taskUUID); err != nil { + var stepErr *pipelineErrors.ErrInvalidWorkflowSetup + if errors.As(err, &stepErr) { + state := new(State) + state.Pipeline.Step = stepErr.Step + state.Pipeline.Error = stepErr.Err + state.Process = &backend.State{ + Error: stepErr.Err, + Exited: true, + ExitCode: 1, + } + + // Trace the error if we have a tracer + if r.tracer != nil { + if err := r.tracer.Trace(state); err != nil { + logger.Error().Err(err).Msg("failed to trace step error") + } + } + } + return err }