Skip to content

Commit 3bf8cd1

Browse files
authored
Merge pull request #1587 from 14rcole/stoneintg-1350-reimplementation
feat(stoneintg-1350): add support for nested componentGroups
2 parents 40bb4d9 + cf4db38 commit 3bf8cd1

28 files changed

Lines changed: 1552 additions & 177 deletions

api/v1beta2/componentgroup_types.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@ type ComponentGroupSpec struct {
4141
// +required
4242
Components []ComponentReference `json:"components"`
4343

44-
// Dependents is a list of ComponentGroup names that are dependent on this ComponentGroup.
45-
// When a snapshot is created for this ComponentGroup, snapshots will also be created for all dependents.
46-
// +optional
47-
Dependents []string `json:"dependents,omitempty"`
48-
4944
// TestGraph describes the desired order in which tests associated with the ComponentGroup should be executed.
5045
// If not specified, all tests will run in parallel.
5146
// The map key is the test scenario name, and the value is a list of parent test scenarios it depends on.
@@ -58,17 +53,24 @@ type ComponentGroupSpec struct {
5853
SnapshotCreator *SnapshotCreatorSpec `json:"snapshotCreator,omitempty"`
5954
}
6055

61-
// ComponentReference references a Component and its specific branch/version
56+
// ComponentReference references a Component or ComponentGroup and its specific branch/version
6257
type ComponentReference struct {
63-
// Name is the name of the Component
58+
// Name is the name of the Component or ComponentGroup
6459
// +kubebuilder:validation:Pattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
6560
// +required
6661
Name string `json:"name"`
6762

63+
// Kind is the type of resource being referenced
64+
// Must be 'component' or 'componentGroup', case-insensitive
65+
// +kubebuilder:default:="component"
66+
// +kubebuilder:validation:Pattern=`(?i)^(component|componentGroup)$`
67+
Kind string `json:"kind,omitempty"`
68+
6869
// ComponentVersion references the ComponentVersion for this Component.
70+
// Can only be set if 'Kind' is set to 'Component' or empty
6971
// The ComponentVersion CRD will be implemented by the build team as part of STONEBLD-3604.
7072
// For now, this contains the branch name and GCL (Global Candidate List) information.
71-
// +required
73+
// +optional
7274
ComponentVersion ComponentVersionReference `json:"componentVersion"`
7375
}
7476

api/v1beta2/componentgroup_types_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ func TestComponentGroupSpec(t *testing.T) {
5252
},
5353
},
5454
},
55-
Dependents: []string{"child-cg-1", "child-cg-2"},
5655
TestGraph: map[string][]TestGraphNode{
5756
"verify": {
5857
{Name: "clamav-scan"},
@@ -116,10 +115,6 @@ func TestComponentGroupSpec(t *testing.T) {
116115
t.Errorf("Expected second component branch 'components/second-component', got '%s'", cg.Spec.Components[1].ComponentVersion.Name)
117116
}
118117

119-
if len(cg.Spec.Dependents) != 2 {
120-
t.Errorf("Expected 2 dependents, got %d", len(cg.Spec.Dependents))
121-
}
122-
123118
if len(cg.Spec.TestGraph) != 2 {
124119
t.Errorf("Expected 2 test graph entries, got %d", len(cg.Spec.TestGraph))
125120
}
@@ -192,9 +187,6 @@ func TestComponentGroupMinimalSpec(t *testing.T) {
192187
if cg.Spec.Components[0].ComponentVersion.Revision != "" {
193188
t.Errorf("Expected empty Revision, got '%s'", cg.Spec.Components[0].ComponentVersion.Revision)
194189
}
195-
if len(cg.Spec.Dependents) != 0 {
196-
t.Errorf("Expected 0 dependents, got %d", len(cg.Spec.Dependents))
197-
}
198190
if cg.Spec.TestGraph != nil {
199191
t.Error("Expected nil TestGraph")
200192
}
@@ -257,7 +249,6 @@ func TestComponentGroupDeepCopy(t *testing.T) {
257249
},
258250
},
259251
},
260-
Dependents: []string{"dependent-1"},
261252
},
262253
}
263254

@@ -360,7 +351,6 @@ func TestComponentGroupSpecDeepCopy(t *testing.T) {
360351
Components: []ComponentReference{
361352
{Name: "comp-1", ComponentVersion: ComponentVersionReference{Name: "main"}},
362353
},
363-
Dependents: []string{"dep-1"},
364354
TestGraph: map[string][]TestGraphNode{
365355
"test-1": {{Name: "node-1", FailFast: true}},
366356
},

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/appstudio.redhat.com_componentgroups.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ spec:
5454
Components is a list of Components (name and branch) that belong to the ComponentGroup.
5555
This is the source of truth for logical groupings of versioned Components.
5656
items:
57-
description: ComponentReference references a Component and its specific
58-
branch/version
57+
description: ComponentReference references a Component or ComponentGroup
58+
and its specific branch/version
5959
properties:
6060
componentVersion:
6161
description: |-
6262
ComponentVersion references the ComponentVersion for this Component.
63+
Can only be set if 'Kind' is set to 'Component' or empty
6364
The ComponentVersion CRD will be implemented by the build team as part of STONEBLD-3604.
6465
For now, this contains the branch name and GCL (Global Candidate List) information.
6566
properties:
@@ -82,22 +83,21 @@ spec:
8283
required:
8384
- name
8485
type: object
86+
kind:
87+
default: component
88+
description: |-
89+
Kind is the type of resource being referenced
90+
Must be 'component' or 'componentGroup', case-insensitive
91+
pattern: (?i)^(component|componentGroup)$
92+
type: string
8593
name:
86-
description: Name is the name of the Component
94+
description: Name is the name of the Component or ComponentGroup
8795
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
8896
type: string
8997
required:
90-
- componentVersion
9198
- name
9299
type: object
93100
type: array
94-
dependents:
95-
description: |-
96-
Dependents is a list of ComponentGroup names that are dependent on this ComponentGroup.
97-
When a snapshot is created for this ComponentGroup, snapshots will also be created for all dependents.
98-
items:
99-
type: string
100-
type: array
101101
snapshotCreator:
102102
description: |-
103103
SnapshotCreator is an optional field that allows custom logic for Snapshot creation.

config/samples/appstudio_v1beta2_componentgroup.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,16 @@ spec:
2424
componentVersion:
2525
name: "main"
2626
- name: second-component
27+
kind: component
2728
componentBranch:
2829
name: "v1"
2930
revision: "v1branch"
3031
context: "components/second-component"
3132
- name: python-component
3233
componentBranch:
3334
name: "3.12.4"
34-
dependents:
35-
# when a snapshot for this ComponentGroup is created,
36-
# the integration service will also create one for child-cg
37-
- child-cg
35+
- name: child-cg
36+
kind: componentGroup
3837
testGraph:
3938
# in this graph clamav-scan, dast-tests, and deployment would run first
4039
# e2e-test would start when clamav-scan finishes

gitops/snapshot.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ const (
251251
// SnapshotAutoReleasedCondition is the condition for marking if Snapshot was auto-released released with AppStudio.
252252
SnapshotAutoReleasedCondition = "AutoReleased"
253253

254+
// ParentSnapshotsCreatedCondition is the condition marking whether snapshots for all parent ComponentGroups for the
255+
// ComponentGroup that the snapshot belongs to have been created
256+
ParentSnapshotsCreatedCondition = "ParentSnapshotsCreated"
257+
254258
// SnapshotAddedToGlobalCandidateListCondition is the condition for marking if Snapshot's component was added to
255259
// the global candidate list.
256260
SnapshotAddedToGlobalCandidateListCondition = "AddedToGlobalCandidateList"
@@ -328,6 +332,10 @@ const (
328332
GitCommentPolicyAnnotation = "test.appstudio.openshift.io/comment_strategy"
329333
// GitCommentPolicyAllDisabled is the value to disable all test comments for the component got pac repository
330334
GitCommentPolicyAllDisabled = "disable_all"
335+
336+
// ChildSnapshotAnnotation is used in nested snapshots. Denotes the name of the child snapshot whose creation triggered the creation of the snapshot
337+
// on which the annotation is set
338+
ChildSnapshotAnnotation = TestLabelPrefix + "/child-snapshot"
331339
)
332340

333341
var (
@@ -670,6 +678,31 @@ func MarkSnapshotAsAutoReleased(ctx context.Context, adapterClient client.Client
670678
return nil
671679
}
672680

681+
func ParentSnapshotsCreated(snapshot *applicationapiv1alpha1.Snapshot) bool {
682+
return IsSnapshotStatusConditionSet(snapshot, ParentSnapshotsCreatedCondition, metav1.ConditionTrue, "")
683+
}
684+
685+
func SetParentSnapshotsCreatedCondition(snapshot *applicationapiv1alpha1.Snapshot, status metav1.ConditionStatus, reason, message string) {
686+
condition := metav1.Condition{
687+
Type: ParentSnapshotsCreatedCondition,
688+
Status: status,
689+
Reason: reason,
690+
Message: message,
691+
}
692+
meta.SetStatusCondition(&snapshot.Status.Conditions, condition)
693+
}
694+
695+
func AddParentSnapshotDataToSnapshotStatus(snapshot *applicationapiv1alpha1.Snapshot, created bool, parentCG, parentSnapshot, message string) {
696+
if snapshot.Status.ParentSnapshots == nil {
697+
snapshot.Status.ParentSnapshots = make(map[string]applicationapiv1alpha1.ParentSnapshotData)
698+
}
699+
snapshot.Status.ParentSnapshots[parentCG] = applicationapiv1alpha1.ParentSnapshotData{
700+
Created: created,
701+
Name: parentSnapshot,
702+
Message: message,
703+
}
704+
}
705+
673706
// IsSnapshotMarkedAsAddedToGlobalCandidateList returns true if snapshot's AddedToGlobalCandidateListAnnotation result is marked as true to global candidate list
674707
func IsSnapshotMarkedAsAddedToGlobalCandidateList(snapshot *applicationapiv1alpha1.Snapshot) bool {
675708
annotationValue, ok := snapshot.GetAnnotations()[AddedToGlobalCandidateListAnnotation]

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.25.0
44

55
require (
66
codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v3 v3.0.0
7-
github.com/konflux-ci/application-api v0.0.0-20260312190025-5154ad273e17
7+
github.com/konflux-ci/application-api v0.0.0-20260603073049-dd8c9b1a64c2
88
github.com/konflux-ci/coverport/instrumentation/go v0.0.0-20251127115143-b5207b335f8b
99
github.com/konflux-ci/image-controller v0.0.0-20241128141349-9986c9955e05
1010
github.com/konflux-ci/operator-toolkit v0.0.0-20251118152634-b4f41f073069

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3J
351351
github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0=
352352
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
353353
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
354-
github.com/konflux-ci/application-api v0.0.0-20260312190025-5154ad273e17 h1:auNv0idITCdlV3fP9i+V6EeT2MbZc/9+05GtKzOk1Ss=
355-
github.com/konflux-ci/application-api v0.0.0-20260312190025-5154ad273e17/go.mod h1:948Z+a1IbfRT0RtoHzWWSN9YEucSbMJTHaMhz7dVICc=
354+
github.com/konflux-ci/application-api v0.0.0-20260603073049-dd8c9b1a64c2 h1:ldBiggDxskW+X1Edqa1REuonbCo+hVmdwA4bC8v674Q=
355+
github.com/konflux-ci/application-api v0.0.0-20260603073049-dd8c9b1a64c2/go.mod h1:948Z+a1IbfRT0RtoHzWWSN9YEucSbMJTHaMhz7dVICc=
356356
github.com/konflux-ci/coverport/instrumentation/go v0.0.0-20251127115143-b5207b335f8b h1:NoriO1KRc+7d2/JA07JizqxP0LlA2oJdD5AuQOvEIjE=
357357
github.com/konflux-ci/coverport/instrumentation/go v0.0.0-20251127115143-b5207b335f8b/go.mod h1:WVMHU9A2464s/vjH1xOTm4LJDD4xP+VlEiU+KM0gkSU=
358358
github.com/konflux-ci/image-controller v0.0.0-20241128141349-9986c9955e05 h1:5Xawkybl99uEiXhkdkxWtHDWitgnf+kAjpNVTanVGRE=

internal/controller/buildpipeline/buildpipeline_adapter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ func (a *Adapter) EnsureSnapshotExists() (result controller.OperationResult, err
219219
}
220220

221221
for _, componentGroup := range *a.componentGroups {
222-
expectedSnapshot, err := snapshot.PrepareSnapshotForPipelineRun(a.context, a.client, a.pipelineRun, a.component.Name, &componentGroup)
222+
expectedSnapshot, err := snapshot.PrepareSnapshotForPipelineRun(a.context, a.client, a.pipelineRun, a.component.Name, &componentGroup, a.loader)
223223
if err != nil {
224224
return a.updatePipelineRunWithCustomizedError(&canRemoveFinalizer, err, a.context, a.pipelineRun, a.client, a.logger)
225225
}

internal/controller/buildpipeline/buildpipeline_adapter_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
664664
ContextKey: loader.ComponentGroupContextKey,
665665
Resource: hasCompGroup,
666666
},
667+
{
668+
ContextKey: loader.NestedComponentGroupsContextKey,
669+
Resource: []v1beta2.ComponentGroup{},
670+
},
667671
{
668672
ContextKey: loader.ComponentContextKey,
669673
Resource: hasComp,
@@ -804,6 +808,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
804808
ContextKey: loader.ComponentGroupContextKey,
805809
Resource: hasCompGroup,
806810
},
811+
{
812+
ContextKey: loader.NestedComponentGroupsContextKey,
813+
Resource: []v1beta2.ComponentGroup{},
814+
},
807815
{
808816
ContextKey: loader.ComponentContextKey,
809817
Resource: hasComp,
@@ -823,7 +831,7 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
823831
},
824832
},
825833
})
826-
_, err := snapshot.PrepareSnapshotForPipelineRun(adapter.context, adapter.client, adapter.pipelineRun, adapter.component.Name, hasCompGroup)
834+
_, err := snapshot.PrepareSnapshotForPipelineRun(adapter.context, adapter.client, adapter.pipelineRun, adapter.component.Name, hasCompGroup, adapter.loader)
827835
Expect(helpers.IsInvalidImageDigestError(err)).To(BeTrue())
828836
Eventually(func() bool {
829837
result, err := adapter.EnsureSnapshotExists()
@@ -1386,6 +1394,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
13861394
ContextKey: loader.ComponentGroupContextKey,
13871395
Resource: hasCompGroup,
13881396
},
1397+
{
1398+
ContextKey: loader.NestedComponentGroupsContextKey,
1399+
Resource: []v1beta2.ComponentGroup{},
1400+
},
13891401
{
13901402
ContextKey: loader.ComponentContextKey,
13911403
Resource: hasComp,
@@ -1426,6 +1438,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
14261438
ContextKey: loader.ComponentGroupContextKey,
14271439
Resource: hasCompGroup,
14281440
},
1441+
{
1442+
ContextKey: loader.NestedComponentGroupsContextKey,
1443+
Resource: []v1beta2.ComponentGroup{},
1444+
},
14291445
{
14301446
ContextKey: loader.ComponentContextKey,
14311447
Resource: hasComp,
@@ -1804,6 +1820,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
18041820
hasCompGroup.Name: nil,
18051821
},
18061822
},
1823+
{
1824+
ContextKey: loader.NestedComponentGroupsContextKey,
1825+
Resource: []v1beta2.ComponentGroup{},
1826+
},
18071827
})
18081828

18091829
Eventually(func() bool {
@@ -2091,6 +2111,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
20912111
Resource: nil,
20922112
Err: notFoundErr,
20932113
},
2114+
{
2115+
ContextKey: loader.NestedComponentGroupsContextKey,
2116+
Resource: []v1beta2.ComponentGroup{},
2117+
},
20942118
})
20952119

20962120
Eventually(func() bool {
@@ -2126,6 +2150,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
21262150
Resource: buildPipelineRun,
21272151
Err: conflictErr,
21282152
},
2153+
{
2154+
ContextKey: loader.NestedComponentGroupsContextKey,
2155+
Resource: []v1beta2.ComponentGroup{},
2156+
},
21292157
})
21302158

21312159
Eventually(func() bool {
@@ -2167,6 +2195,10 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
21672195
ContextKey: loader.AllSnapshotsContextKey,
21682196
Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot},
21692197
},
2198+
{
2199+
ContextKey: loader.NestedComponentGroupsContextKey,
2200+
Resource: []v1beta2.ComponentGroup{},
2201+
},
21702202
})
21712203

21722204
Eventually(func() bool {

0 commit comments

Comments
 (0)