Skip to content

Commit c94f612

Browse files
authored
Merge pull request #1092 from 14rcole/stoneintg-1165
feat(stoneintg-1165): resolve pipelineruns from scenario
2 parents 0749305 + 7781fc6 commit c94f612

25 files changed

+1131
-20
lines changed

config/rbac/role.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ rules:
8181
- get
8282
- list
8383
- watch
84+
- apiGroups:
85+
- resolution.tekton.dev
86+
resources:
87+
- resolutionrequests
88+
verbs:
89+
- create
90+
- delete
91+
- get
8492
- apiGroups:
8593
- tekton.dev
8694
resources:

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ require (
1919
github.com/prometheus/client_golang v1.21.1
2020
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
2121
github.com/tektoncd/pipeline v0.68.0
22+
github.com/tektoncd/resolution v0.0.0-20220901133656-d680719abeaf
2223
github.com/tonglil/buflogr v1.1.1
2324
github.com/xanzy/go-gitlab v0.109.0
2425
go.uber.org/mock v0.5.0
2526
go.uber.org/zap v1.27.0
2627
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
2728
golang.org/x/oauth2 v0.25.0
29+
gopkg.in/yaml.v3 v3.0.1
2830
k8s.io/api v0.29.13
2931
k8s.io/apimachinery v0.29.13
3032
k8s.io/client-go v1.5.2
@@ -125,7 +127,6 @@ require (
125127
google.golang.org/protobuf v1.36.4 // indirect
126128
gopkg.in/inf.v0 v0.9.1 // indirect
127129
gopkg.in/yaml.v2 v2.4.0 // indirect
128-
gopkg.in/yaml.v3 v3.0.1 // indirect
129130
k8s.io/apiextensions-apiserver v0.29.3 // indirect
130131
k8s.io/apiserver v0.29.3 // indirect
131132
k8s.io/component-base v0.29.3 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf
369369
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
370370
github.com/tektoncd/pipeline v0.68.0 h1:bVzj+HbS/NQAPV2CfEW8HZrREm7uagPCzEggyxVTBdc=
371371
github.com/tektoncd/pipeline v0.68.0/go.mod h1:MScUtGGW4VeaNcaNrulmNLBSn14EGTXXKy+kh+YZ8Gg=
372+
github.com/tektoncd/resolution v0.0.0-20220901133656-d680719abeaf h1:IS5bXIlgC6uo61zZP5GBCQ+FoNnRIa0G0ptJh2OFtj4=
373+
github.com/tektoncd/resolution v0.0.0-20220901133656-d680719abeaf/go.mod h1:kPG4H5o69yaGOWfQ4BwtyykUtPWeSeAhfMWKV0gH+Eo=
372374
github.com/tonglil/buflogr v1.1.1 h1:CKAjOHBSMmgbRFxpn/RhQHPj5oANc7ekhlsoUDvcZIg=
373375
github.com/tonglil/buflogr v1.1.1/go.mod h1:WLLtPRLqcFYWQLbA+ytXy5WrFTYnfA+beg1MpvJCxm4=
374376
github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE=

internal/controller/snapshot/snapshot_adapter.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -762,8 +762,12 @@ func (a *Adapter) createIntegrationPipelineRun(application *applicationapiv1alph
762762
a.logger.Info("Creating new pipelinerun for integrationTestscenario",
763763
"integrationTestScenario.Name", integrationTestScenario.Name)
764764

765-
pipelineRunBuilder := tekton.NewIntegrationPipelineRun(integrationTestScenario.Name, application.Namespace, *integrationTestScenario).
766-
WithSnapshot(snapshot).
765+
pipelineRunBuilder, err := tekton.NewIntegrationPipelineRun(a.client, a.context, integrationTestScenario.Name, application.Namespace, *integrationTestScenario)
766+
if err != nil {
767+
return nil, err
768+
}
769+
770+
pipelineRunBuilder = pipelineRunBuilder.WithSnapshot(snapshot).
767771
WithIntegrationLabels(integrationTestScenario).
768772
WithIntegrationAnnotations(integrationTestScenario).
769773
WithApplication(a.application).
@@ -777,7 +781,7 @@ func (a *Adapter) createIntegrationPipelineRun(application *applicationapiv1alph
777781

778782
pipelineRun := pipelineRunBuilder.AsPipelineRun()
779783

780-
err := ctrl.SetControllerReference(snapshot, pipelineRun, a.client.Scheme())
784+
err = ctrl.SetControllerReference(snapshot, pipelineRun, a.client.Scheme())
781785
if err != nil {
782786
return nil, fmt.Errorf("failed to set snapshot %s as ControllerReference of pipelineRun: %w", snapshot.Name, err)
783787
}

internal/controller/snapshot/snapshot_controller.go

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func NewSnapshotReconciler(client client.Client, logger *logr.Logger, scheme *ru
6868
//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=releases,verbs=create;delete;get;list;patch;update;watch
6969
//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=releaseplans,verbs=get;list;watch
7070
//+kubebuilder:rbac:groups=appstudio.redhat.com,resources=releaseplans/status,verbs=get;update;patch
71+
//+kubebuilder:rbac:groups=resolution.tekton.dev,resources=resolutionrequests,verbs=create;get;delete
7172

7273
// Reconcile is part of the main kubernetes reconciliation loop which aims to
7374
// move the current state of the cluster closer to the desired state.

tekton/integration_pipeline.go

+117-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package tekton
1818

1919
import (
20+
"context"
21+
"encoding/base64"
2022
"encoding/json"
2123
"fmt"
2224
"strings"
@@ -29,7 +31,15 @@ import (
2931
"github.com/konflux-ci/integration-service/api/v1beta2"
3032
"github.com/konflux-ci/operator-toolkit/metadata"
3133
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
34+
resolutionv1alpha1 "github.com/tektoncd/resolution/pkg/apis/resolution/v1alpha1"
35+
yaml "gopkg.in/yaml.v3"
36+
corev1 "k8s.io/api/core/v1"
3237
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
38+
"k8s.io/apimachinery/pkg/types"
39+
"k8s.io/apimachinery/pkg/util/wait"
40+
"k8s.io/client-go/util/retry"
41+
knative "knative.dev/pkg/apis"
42+
"sigs.k8s.io/controller-runtime/pkg/client"
3343
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3444
)
3545

@@ -63,6 +73,12 @@ const (
6373

6474
// Name of tekton git resolver param revision
6575
TektonResolverGitParamRevision = "revision"
76+
77+
// Value of ResourceKind field for remote pipelines
78+
ResourceKindPipeline = "pipeline"
79+
80+
// Value of ResourceKind field for remote pipelineruns
81+
ResourceKindPipelineRun = "pipelinerun"
6682
)
6783

6884
var (
@@ -103,18 +119,35 @@ func (r *IntegrationPipelineRun) AsPipelineRun() *tektonv1.PipelineRun {
103119

104120
// NewIntegrationPipelineRun creates an empty PipelineRun in the given namespace. The name will be autogenerated,
105121
// using the prefix passed as an argument to the function.
106-
func NewIntegrationPipelineRun(prefix, namespace string, integrationTestScenario v1beta2.IntegrationTestScenario) *IntegrationPipelineRun {
107-
resolverParams := []tektonv1.Param{}
122+
func NewIntegrationPipelineRun(client client.Client, ctx context.Context, prefix, namespace string, integrationTestScenario v1beta2.IntegrationTestScenario) (*IntegrationPipelineRun, error) {
123+
resolverParams := integrationTestScenario.Spec.ResolverRef.Params
124+
125+
resolver := integrationTestScenario.Spec.ResolverRef.Resolver
126+
resourceKind := integrationTestScenario.Spec.ResolverRef.ResourceKind
127+
if resourceKind == ResourceKindPipeline || resourceKind == "" {
128+
return generateIntegrationPipelineRunFromPipelineResolver(prefix, namespace, resolver, resolverParams), nil
129+
} else if resourceKind == ResourceKindPipelineRun {
130+
base64plr, err := getPipelineRunYamlFromPipelineRunResolver(client, ctx, prefix, namespace, resolver, resolverParams)
131+
if err != nil {
132+
return nil, err
133+
}
134+
return generateIntegrationPipelineRunFromBase64(base64plr)
135+
} else {
136+
return nil, fmt.Errorf("unrecognized resolver type '%s'", resourceKind)
137+
}
138+
}
108139

109-
for _, scenarioParam := range integrationTestScenario.Spec.ResolverRef.Params {
110-
resolverParam := tektonv1.Param{
140+
func generateIntegrationPipelineRunFromPipelineResolver(prefix, namespace, resolver string, resolverParams []v1beta2.ResolverParameter) *IntegrationPipelineRun {
141+
tektonResolverParams := []tektonv1.Param{}
142+
for _, scenarioParam := range resolverParams {
143+
tektonResolverParam := tektonv1.Param{
111144
Name: scenarioParam.Name,
112145
Value: tektonv1.ParamValue{
113146
Type: tektonv1.ParamTypeString,
114147
StringVal: scenarioParam.Value,
115148
},
116149
}
117-
resolverParams = append(resolverParams, resolverParam)
150+
tektonResolverParams = append(tektonResolverParams, tektonResolverParam)
118151
}
119152

120153
pipelineRun := tektonv1.PipelineRun{
@@ -125,15 +158,92 @@ func NewIntegrationPipelineRun(prefix, namespace string, integrationTestScenario
125158
Spec: tektonv1.PipelineRunSpec{
126159
PipelineRef: &tektonv1.PipelineRef{
127160
ResolverRef: tektonv1.ResolverRef{
128-
Resolver: tektonv1.ResolverName(integrationTestScenario.Spec.ResolverRef.Resolver),
129-
Params: resolverParams,
161+
Resolver: tektonv1.ResolverName(resolver),
162+
Params: tektonResolverParams,
130163
},
131164
},
132165
},
133166
}
167+
134168
return &IntegrationPipelineRun{pipelineRun}
135169
}
136170

171+
func getPipelineRunYamlFromPipelineRunResolver(client client.Client, ctx context.Context, prefix, namespace, resolver string, resolverParams []v1beta2.ResolverParameter) (string, error) {
172+
stringResolverParams := map[string]string{}
173+
174+
for _, scenarioParam := range resolverParams {
175+
stringResolverParams[scenarioParam.Name] = scenarioParam.Value
176+
}
177+
178+
request := resolutionv1alpha1.ResolutionRequest{
179+
ObjectMeta: metav1.ObjectMeta{
180+
GenerateName: prefix + "-",
181+
Namespace: namespace,
182+
Labels: map[string]string{
183+
"resolution.tekton.dev/type": resolver,
184+
"konflux-ci.dev/created-by": "integration-service", // for backup garbage collection
185+
},
186+
},
187+
Spec: resolutionv1alpha1.ResolutionRequestSpec{
188+
Parameters: stringResolverParams,
189+
},
190+
}
191+
192+
err := retry.OnError(retry.DefaultBackoff, func(e error) bool { return true }, func() error {
193+
return client.Create(ctx, &request)
194+
})
195+
if err != nil {
196+
return "", err
197+
}
198+
resolutionRequestName := request.ObjectMeta.Name
199+
defer func() {
200+
_ = retry.OnError(retry.DefaultBackoff, func(e error) bool { return true }, func() error {
201+
return client.Delete(ctx, &request)
202+
})
203+
}()
204+
205+
resolverBackoff := wait.Backoff{
206+
Steps: 60,
207+
Duration: 1 * time.Second,
208+
Factor: 1.0,
209+
Jitter: 0.5,
210+
}
211+
var resolvedRequest resolutionv1alpha1.ResolutionRequest
212+
err = retry.OnError(resolverBackoff, func(e error) bool { return true }, func() error {
213+
err = client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: resolutionRequestName}, &resolvedRequest)
214+
if err != nil {
215+
return err
216+
}
217+
for _, cond := range resolvedRequest.Status.Conditions {
218+
if cond.Type == knative.ConditionSucceeded {
219+
if cond.Status == corev1.ConditionTrue {
220+
// request resolved successfully, we can terminate retry block
221+
return nil
222+
}
223+
}
224+
}
225+
return fmt.Errorf("resolution for '%s' in namespace '%s' did not complete", namespace, resolutionRequestName)
226+
})
227+
228+
if err != nil {
229+
return "", err
230+
}
231+
return resolvedRequest.Status.Data, nil
232+
}
233+
234+
func generateIntegrationPipelineRunFromBase64(base64plr string) (*IntegrationPipelineRun, error) {
235+
plrYaml, err := base64.StdEncoding.DecodeString(base64plr)
236+
if err != nil {
237+
return nil, err
238+
}
239+
var pipelineRun tektonv1.PipelineRun
240+
err = yaml.Unmarshal(plrYaml, &pipelineRun)
241+
if err != nil {
242+
return nil, err
243+
}
244+
return &IntegrationPipelineRun{pipelineRun}, nil
245+
}
246+
137247
// Updates git resolver values parameters with values of params specified in the input map
138248
// updates only exsitings parameters, doens't create new ones
139249
func (iplr *IntegrationPipelineRun) WithUpdatedTestsGitResolver(params map[string]string) *IntegrationPipelineRun {

tekton/integration_pipeline_test.go

+25-9
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ var _ = Describe("Integration pipeline", func() {
8282
Spec: v1beta2.IntegrationTestScenarioSpec{
8383
Application: "application-sample",
8484
ResolverRef: v1beta2.ResolverRef{
85-
Resolver: "git",
85+
ResourceKind: tekton.ResourceKindPipeline,
86+
Resolver: "git",
8687
Params: []v1beta2.ResolverParameter{
8788
{
8889
Name: "url",
@@ -103,7 +104,9 @@ var _ = Describe("Integration pipeline", func() {
103104
Expect(k8sClient.Create(ctx, integrationTestScenarioGit)).Should(Succeed())
104105

105106
//create new integration pipeline run from integration test scenario
106-
newIntegrationPipelineRun = tekton.NewIntegrationPipelineRun(prefix, namespace, *integrationTestScenarioGit)
107+
var err error
108+
newIntegrationPipelineRun, err = tekton.NewIntegrationPipelineRun(k8sClient, ctx, prefix, namespace, *integrationTestScenarioGit)
109+
Expect(err).NotTo(HaveOccurred())
107110
Expect(k8sClient.Create(ctx, newIntegrationPipelineRun.AsPipelineRun())).Should(Succeed())
108111

109112
integrationTestScenarioBundle = &v1beta2.IntegrationTestScenario{
@@ -118,7 +121,8 @@ var _ = Describe("Integration pipeline", func() {
118121
Spec: v1beta2.IntegrationTestScenarioSpec{
119122
Application: "application-sample",
120123
ResolverRef: v1beta2.ResolverRef{
121-
Resolver: "bundles",
124+
ResourceKind: tekton.ResourceKindPipeline,
125+
Resolver: "bundles",
122126
Params: []v1beta2.ResolverParameter{
123127
{
124128
Name: "bundle",
@@ -154,7 +158,8 @@ var _ = Describe("Integration pipeline", func() {
154158
Spec: v1beta2.IntegrationTestScenarioSpec{
155159
Application: "application-sample",
156160
ResolverRef: v1beta2.ResolverRef{
157-
Resolver: "git",
161+
ResourceKind: tekton.ResourceKindPipeline,
162+
Resolver: "git",
158163
Params: []v1beta2.ResolverParameter{
159164
{
160165
Name: "url",
@@ -234,14 +239,16 @@ var _ = Describe("Integration pipeline", func() {
234239
}
235240
Expect(k8sClient.Create(ctx, hasComp)).Should(Succeed())
236241

237-
newIntegrationBundlePipelineRun = tekton.NewIntegrationPipelineRun(prefix, namespace, *integrationTestScenarioBundle).
238-
WithIntegrationLabels(integrationTestScenarioBundle).
242+
newIntegrationBundlePipelineRun, err = tekton.NewIntegrationPipelineRun(k8sClient, ctx, prefix, namespace, *integrationTestScenarioBundle)
243+
Expect(err).NotTo(HaveOccurred())
244+
newIntegrationBundlePipelineRun = newIntegrationBundlePipelineRun.WithIntegrationLabels(integrationTestScenarioBundle).
239245
WithSnapshot(hasSnapshot).
240246
WithApplication(hasApp)
241247
Expect(k8sClient.Create(ctx, newIntegrationBundlePipelineRun.AsPipelineRun())).Should(Succeed())
242248

243-
enterpriseContractPipelineRun = tekton.NewIntegrationPipelineRun(prefix, namespace, *enterpriseContractTestScenario).
244-
WithIntegrationLabels(enterpriseContractTestScenario).
249+
enterpriseContractPipelineRun, err = tekton.NewIntegrationPipelineRun(k8sClient, ctx, prefix, namespace, *enterpriseContractTestScenario)
250+
Expect(err).NotTo(HaveOccurred())
251+
enterpriseContractPipelineRun = enterpriseContractPipelineRun.WithIntegrationLabels(enterpriseContractTestScenario).
245252
WithIntegrationAnnotations(enterpriseContractTestScenario).
246253
WithSnapshot(hasSnapshot).
247254
WithExtraParams(enterpriseContractTestScenario.Spec.Params).
@@ -257,19 +264,28 @@ var _ = Describe("Integration pipeline", func() {
257264
_ = k8sClient.Delete(ctx, integrationTestScenarioGit)
258265
_ = k8sClient.Delete(ctx, integrationTestScenarioBundle)
259266
_ = k8sClient.Delete(ctx, enterpriseContractTestScenario)
267+
_ = k8sClient.Delete(ctx, newIntegrationPipelineRun.AsPipelineRun())
260268
_ = k8sClient.Delete(ctx, newIntegrationPipelineRun)
261269
_ = k8sClient.Delete(ctx, newIntegrationBundlePipelineRun)
262270
_ = k8sClient.Delete(ctx, enterpriseContractPipelineRun)
263271
_ = k8sClient.Delete(ctx, hasApp)
264272
_ = k8sClient.Delete(ctx, hasSnapshot)
265273
_ = k8sClient.Delete(ctx, hasComp)
266-
_ = k8sClient.Delete(ctx, newIntegrationPipelineRun.AsPipelineRun())
267274

268275
os.Setenv("PIPELINE_TIMEOUT", "")
269276
os.Setenv("TASKS_TIMEOUT", "")
270277
os.Setenv("FINALLY_TIMEOUT", "")
271278
})
272279

280+
Context("When creating a new IntegrationPipelineRun", func() {
281+
It("can create an IntegrationPipelineRun", func() {
282+
plr, err := tekton.NewIntegrationPipelineRun(k8sClient, ctx, prefix, namespace, *integrationTestScenarioGit)
283+
Expect(err).NotTo(HaveOccurred())
284+
Expect(plr).NotTo(BeNil())
285+
Expect(k8sClient.Create(ctx, plr.AsPipelineRun())).Should(Succeed())
286+
})
287+
})
288+
273289
Context("When managing a new IntegrationPipelineRun", func() {
274290
It("can create a IntegrationPipelineRun and the returned object name is prefixed with the provided GenerateName", func() {
275291
Expect(newIntegrationPipelineRun.ObjectMeta.Name).

tekton/tekton_suite_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
applicationapiv1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
4040
releasev1alpha1 "github.com/konflux-ci/release-service/api/v1alpha1"
4141
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
42+
resolutionv1alpha1 "github.com/tektoncd/resolution/pkg/apis/resolution/v1alpha1"
4243
clientsetscheme "k8s.io/client-go/kubernetes/scheme"
4344
"sigs.k8s.io/controller-runtime/pkg/client"
4445
"sigs.k8s.io/controller-runtime/pkg/envtest"
@@ -88,6 +89,7 @@ var _ = BeforeSuite(func() {
8889

8990
Expect(applicationapiv1alpha1.AddToScheme(clientsetscheme.Scheme)).To(Succeed())
9091
Expect(tektonv1.AddToScheme(clientsetscheme.Scheme)).To(Succeed())
92+
Expect(resolutionv1alpha1.AddToScheme(clientsetscheme.Scheme)).To(Succeed())
9193
Expect(releasev1alpha1.AddToScheme(clientsetscheme.Scheme)).To(Succeed())
9294
Expect(v1beta2.AddToScheme(clientsetscheme.Scheme)).To(Succeed())
9395

0 commit comments

Comments
 (0)