Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/user-guide/global-configurations/build-infra.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ If you delete a profile attached to one or more applications, the [default profi

If you need extra control on the build infra configuration apart from CPU, memory, and build timeout, feel free to open a [GitHub issue](https://github.com/devtron-labs/devtron/issues) for us to help you.

### Pipeline-Level Build Infra Selection

In addition to application-level build infra profiles, you can now assign specific build infrastructure profiles at the individual CI pipeline level. This provides more granular control for scenarios where:

1. **Production pipelines** require different build infrastructure than development pipelines
2. **Different build types** (e.g., security scanning vs. fast builds) need specific resource configurations
3. **Environment-specific builds** require tailored infrastructure settings

**How it works:**
- When creating or editing a CI pipeline, you can optionally select a specific build infra profile
- If a pipeline-level profile is selected, it takes priority over the application-level profile
- If no pipeline-level profile is specified, the system falls back to the application-level profile
- This maintains full backward compatibility with existing configurations

**Use cases:**
- Assign high-resource profiles to production CI pipelines while keeping development pipelines on standard resources
- Use specialized build infrastructure for pipelines that include security scanning or extensive testing
- Optimize costs by using appropriate infrastructure sizes for different types of builds

This feature allows you to define build infrastructure for all production pipelines (CI pipelines attached to production environments) while providing flexibility to pass node-level selectors for development builds as needed.

---

## Extras
Expand Down
19 changes: 19 additions & 0 deletions internal/sql/repository/pipelineConfig/CiPipelineRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type CiPipeline struct {
ScanEnabled bool `sql:"scan_enabled,notnull"`
IsDockerConfigOverridden bool `sql:"is_docker_config_overridden, notnull"`
PipelineType string `sql:"ci_pipeline_type"`
InfraProfileId *int `sql:"infra_profile_id"` // Optional pipeline-level infra profile
sql.AuditLog
CiPipelineMaterials []*CiPipelineMaterial
CiTemplate *CiTemplate
Expand Down Expand Up @@ -146,6 +147,8 @@ type CiPipelineRepository interface {
GetLinkedCiPipelines(ctx context.Context, ciPipelineId int) ([]*CiPipeline, error)
GetDownStreamInfo(ctx context.Context, sourceCiPipelineId int,
appNameMatch, envNameMatch string, req *pagination.RepositoryRequest) ([]ciPipeline.LinkedCIDetails, int, error)
// GetInfraProfileIdByPipelineId gets the infra profile ID assigned to a specific CI pipeline
GetInfraProfileIdByPipelineId(pipelineId int) (*int, error)
}

type CiPipelineRepositoryImpl struct {
Expand Down Expand Up @@ -726,3 +729,19 @@ func (impl *CiPipelineRepositoryImpl) GetDownStreamInfo(ctx context.Context, sou
}
return linkedCIDetails, totalCount, err
}

func (impl *CiPipelineRepositoryImpl) GetInfraProfileIdByPipelineId(pipelineId int) (*int, error) {
var infraProfileId *int
err := impl.dbConnection.Model((*CiPipeline)(nil)).
Where("id = ?", pipelineId).
Where("deleted = ?", false).
Column("infra_profile_id").
Select(&infraProfileId)
if err != nil {
if errors.Is(err, pg.ErrNoRows) {
return nil, nil
}
return nil, err
}
return infraProfileId, nil
}
1 change: 1 addition & 0 deletions pkg/bean/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ type CiPipeline struct {
CustomTagObject *CustomTagData `json:"customTag,omitempty"`
DefaultTag []string `json:"defaultTag,omitempty"`
EnableCustomTag bool `json:"enableCustomTag"`
InfraProfileId *int `json:"infraProfileId,omitempty"` // Optional pipeline-level infra profile override
}

func (ciPipeline *CiPipeline) IsLinkedCi() bool {
Expand Down
3 changes: 2 additions & 1 deletion pkg/build/trigger/HandlerService.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,8 @@ func (impl *HandlerServiceImpl) StartCiWorkflowAndPrepareWfRequest(trigger *type
}

scope := resourceQualifiers.Scope{
AppId: pipeline.App.Id,
AppId: pipeline.App.Id,
PipelineId: pipeline.Id, // Added pipeline ID for pipeline-level infra profile support
}
ciWorkflowConfigNamespace := impl.config.GetDefaultNamespace()
envModal, isJob, err := impl.getEnvironmentForJob(pipeline, trigger)
Expand Down
31 changes: 30 additions & 1 deletion pkg/infraConfig/service/infraConfigService_ent.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/devtron-labs/devtron/pkg/infraConfig/repository"
"github.com/devtron-labs/devtron/pkg/resourceQualifiers"
"github.com/go-pg/pg"
"github.com/pkg/errors"
)

type InfraConfigServiceEnt interface {
Expand Down Expand Up @@ -51,6 +52,34 @@ func (impl *InfraConfigServiceImpl) getDefaultBuildxDriverType() v1.BuildxDriver
}

func (impl *InfraConfigServiceImpl) getInfraProfileIdsByScope(scope *v1.Scope) ([]int, error) {
// for OSS, user can't create infra profiles so no need to fetch infra profiles
// First check if there's a pipeline-level infra profile
if scope.PipelineId > 0 {
pipelineInfraProfileId, err := impl.getPipelineInfraProfileId(scope.PipelineId)
if err != nil {
impl.logger.Errorw("error in fetching pipeline-level infra profile", "pipelineId", scope.PipelineId, "error", err)
return make([]int, 0), err
}
if pipelineInfraProfileId != nil {
impl.logger.Debugw("found pipeline-level infra profile", "pipelineId", scope.PipelineId, "infraProfileId", *pipelineInfraProfileId)
return []int{*pipelineInfraProfileId}, nil
}
}

// For OSS, user can't create infra profiles so no need to fetch infra profiles
// Falls back to global profile
return make([]int, 0), nil
}

func (impl *InfraConfigServiceImpl) getPipelineInfraProfileId(pipelineId int) (*int, error) {
// Query the ci_pipeline table directly for the infra_profile_id
var infraProfileId *int
query := `SELECT infra_profile_id FROM ci_pipeline WHERE id = ? AND deleted = false`
_, err := impl.infraProfileRepo.GetDbConnection().Query(&infraProfileId, query, pipelineId)
Comment on lines +74 to +77
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using raw SQL query instead of the repository pattern breaks the established abstraction. Consider using the CiPipelineRepository.GetInfraProfileIdByPipelineId method that's already implemented in the same PR.

Suggested change
// Query the ci_pipeline table directly for the infra_profile_id
var infraProfileId *int
query := `SELECT infra_profile_id FROM ci_pipeline WHERE id = ? AND deleted = false`
_, err := impl.infraProfileRepo.GetDbConnection().Query(&infraProfileId, query, pipelineId)
// Use the repository method to fetch infra_profile_id for the pipeline
infraProfileId, err := impl.ciPipelineRepository.GetInfraProfileIdByPipelineId(pipelineId)

Copilot uses AI. Check for mistakes.
if err != nil {
if errors.Is(err, pg.ErrNoRows) {
return nil, nil // Pipeline not found, fall back to app-level profile
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment suggests fallback to app-level profile, but returning nil here doesn't trigger the fallback - it just returns nil. The actual fallback logic happens in the calling function when pipelineInfraProfileId is nil.

Suggested change
return nil, nil // Pipeline not found, fall back to app-level profile
return nil, nil // Pipeline not found; returning nil signals caller to handle fallback to app-level profile

Copilot uses AI. Check for mistakes.
}
return nil, err
}
return infraProfileId, nil
}
2 changes: 2 additions & 0 deletions pkg/pipeline/CiCdPipelineOrchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ func (impl CiCdPipelineOrchestratorImpl) PatchMaterialValue(createRequest *bean.
ParentCiPipeline: createRequest.ParentCiPipeline,
ScanEnabled: createRequest.ScanEnabled,
IsDockerConfigOverridden: createRequest.IsDockerConfigOverridden,
InfraProfileId: createRequest.InfraProfileId, // Pipeline-level infra profile support
AuditLog: sql.AuditLog{UpdatedBy: userId, UpdatedOn: time.Now()},
}

Expand Down Expand Up @@ -949,6 +950,7 @@ func (impl CiCdPipelineOrchestratorImpl) CreateCiConf(createRequest *bean.CiConf
ScanEnabled: createRequest.ScanEnabled,
IsDockerConfigOverridden: ciPipeline.IsDockerConfigOverridden,
PipelineType: string(ciPipeline.PipelineType),
InfraProfileId: ciPipeline.InfraProfileId, // Pipeline-level infra profile support
AuditLog: sql.AuditLog{UpdatedBy: createRequest.UserId, CreatedBy: createRequest.UserId, UpdatedOn: time.Now(), CreatedOn: time.Now()},
}
err = impl.ciPipelineRepository.Save(ciPipelineObject, tx)
Expand Down
11 changes: 11 additions & 0 deletions scripts/sql/34604300_pipeline_infra_profile.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Rollback migration for pipeline-level infra profile support

BEGIN;

-- Drop the foreign key constraint
ALTER TABLE ci_pipeline DROP CONSTRAINT IF EXISTS fk_ci_pipeline_infra_profile;

-- Drop the infra_profile_id column
ALTER TABLE ci_pipeline DROP COLUMN IF EXISTS infra_profile_id;

COMMIT;
18 changes: 18 additions & 0 deletions scripts/sql/34604300_pipeline_infra_profile.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Migration to add pipeline-level infra profile support to ci_pipeline table
-- This enables selecting different build infrastructure per pipeline

BEGIN;

-- Add infra_profile_id column to ci_pipeline table
ALTER TABLE ci_pipeline
ADD COLUMN IF NOT EXISTS infra_profile_id INTEGER;

-- Add foreign key constraint to infra_profile table
ALTER TABLE ci_pipeline
ADD CONSTRAINT fk_ci_pipeline_infra_profile
FOREIGN KEY (infra_profile_id) REFERENCES infra_profile(id);

-- Add comment to explain the column
COMMENT ON COLUMN ci_pipeline.infra_profile_id IS 'Optional pipeline-level infra profile override. If null, falls back to application-level profile.';

COMMIT;
Loading