Skip to content

Commit

Permalink
feat(RELEASE-1434): allow to override volume type
Browse files Browse the repository at this point in the history
This is a provisional change that will be used during the
transition to using Trusted Artificts in the
release-service-catalog pipelines.

Trusted Artifacts require to use an emptyDir in opposition
to the PVCs used today. This change allow to override
the volume type used by a given pipeline by defining a map
in the ReleaseServiceConfig which maps a string formed like
"<url>/<pathInRepo>" to a volume type. Those values are only
found in pipeline refs using the git resolver. Any other
resolver gets ignored.

Signed-off-by: David Moreno García <[email protected]>
  • Loading branch information
davidmogar committed Feb 7, 2025
1 parent 960bba4 commit 46ff4ca
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 9 deletions.
8 changes: 8 additions & 0 deletions api/v1alpha1/releaseserviceconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import (

const ReleaseServiceConfigResourceName string = "release-service-config"

// VolumeType is the kind of volume to be used for the managed release PipelineRun referencing this Pipeline.
// +kubebuilder:validation:Enum=emptyDir
type VolumeType string

// ReleaseServiceConfigSpec defines the desired state of ReleaseServiceConfig.
type ReleaseServiceConfigSpec struct {
// Debug is the boolean that specifies whether or not the Release Service should run
Expand All @@ -33,6 +37,10 @@ type ReleaseServiceConfigSpec struct {
// DefaultTimeouts contain the default Tekton timeouts to be used in case they are
// not specified in the ReleasePlanAdmission resource.
DefaultTimeouts tektonv1.TimeoutFields `json:"defaultTimeouts,omitempty"`

// VolumeOverrides is a map containing the volume type for specific Pipeline git refs
// +optional
VolumeOverrides map[string]VolumeType `json:"volumeOverrides,omitempty"`
}

// ReleaseServiceConfigStatus defines the observed state of ReleaseServiceConfig.
Expand Down
7 changes: 7 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions config/crd/bases/appstudio.redhat.com_releaseserviceconfigs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ spec:
tasks
type: string
type: object
volumeOverrides:
additionalProperties:
description: VolumeType is the kind of volume to be used for the
managed release PipelineRun referencing this Pipeline.
enum:
- emptyDir
type: string
description: VolumeOverrides is a map containing the volume type for
specific Pipeline git refs
type: object
type: object
status:
description: ReleaseServiceConfigStatus defines the observed state of
Expand Down
20 changes: 15 additions & 5 deletions controllers/release/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ func (a *adapter) createFinalPipelineRun(releasePlan *v1alpha1.ReleasePlan, snap
// will be extracted from the given ReleasePlanAdmission. The Release's Snapshot will also be passed to the release
// PipelineRun.
func (a *adapter) createManagedPipelineRun(resources *loader.ProcessingResources) (*tektonv1.PipelineRun, error) {
pipelineRun, err := utils.NewPipelineRunBuilder(metadata.ManagedPipelineType, resources.ReleasePlanAdmission.Namespace).
builder := utils.NewPipelineRunBuilder(metadata.ManagedPipelineType, resources.ReleasePlanAdmission.Namespace).
WithAnnotations(metadata.GetAnnotationsWithPrefix(a.release, integrationgitops.PipelinesAsCodePrefix)).
WithFinalizer(metadata.ReleaseFinalizer).
WithLabels(map[string]string{
Expand All @@ -870,13 +870,23 @@ func (a *adapter) createManagedPipelineRun(resources *loader.ProcessingResources
WithParamsFromConfigMap(resources.EnterpriseContractConfigMap, []string{"verify_ec_task_bundle"}).
WithPipelineRef(resources.ReleasePlanAdmission.Spec.Pipeline.PipelineRef.ToTektonPipelineRef()).
WithServiceAccount(resources.ReleasePlanAdmission.Spec.Pipeline.ServiceAccountName).
WithTimeouts(&resources.ReleasePlanAdmission.Spec.Pipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts).
WithWorkspaceFromVolumeTemplate(
WithTimeouts(&resources.ReleasePlanAdmission.Spec.Pipeline.Timeouts, &a.releaseServiceConfig.Spec.DefaultTimeouts)

pipelineGitRef, err := resources.ReleasePlanAdmission.Spec.Pipeline.PipelineRef.GetPipelineGitRef()
if err == nil && a.releaseServiceConfig.Spec.VolumeOverrides[pipelineGitRef] == "emptyDir" {
builder.WithEmptyDirVolume(
os.Getenv("DEFAULT_RELEASE_WORKSPACE_NAME"),
os.Getenv("DEFAULT_RELEASE_WORKSPACE_SIZE"),
).
Build()
)
} else {
builder.WithWorkspaceFromVolumeTemplate(
os.Getenv("DEFAULT_RELEASE_WORKSPACE_NAME"),
os.Getenv("DEFAULT_RELEASE_WORKSPACE_SIZE"),
)
}

var pipelineRun *tektonv1.PipelineRun
pipelineRun, err = builder.Build()
if err != nil {
return nil, err
}
Expand Down
113 changes: 109 additions & 4 deletions controllers/release/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2183,43 +2183,57 @@ var _ = Describe("Release adapter", Ordered, func() {
var (
adapter *adapter
pipelineRun *tektonv1.PipelineRun
resources *loader.ProcessingResources
)

AfterEach(func() {
_ = adapter.client.Delete(ctx, adapter.release)

Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed())
if pipelineRun != nil {
Expect(k8sClient.Delete(ctx, pipelineRun)).To(Succeed())
}
})

BeforeEach(func() {
adapter = createReleaseAndAdapter()
adapter.releaseServiceConfig = releaseServiceConfig
resources := &loader.ProcessingResources{
pipelineRun = nil
resources = &loader.ProcessingResources{
ReleasePlan: releasePlan,
ReleasePlanAdmission: releasePlanAdmission,
EnterpriseContractConfigMap: enterpriseContractConfigMap,
EnterpriseContractPolicy: enterpriseContractPolicy,
Snapshot: snapshot,
}
})

It("returns a PipelineRun with the right prefix", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())
})

It("returns a PipelineRun with the right prefix", func() {
Expect(reflect.TypeOf(pipelineRun)).To(Equal(reflect.TypeOf(&tektonv1.PipelineRun{})))
Expect(pipelineRun.Name).To(HavePrefix("managed"))
})

It("has the release reference", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", strings.ToLower(adapter.release.Kind))))
Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal",
fmt.Sprintf("%s%c%s", adapter.release.Namespace, types.Separator, adapter.release.Name))))
})

It("has the releasePlan reference", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

name := []rune(releasePlan.Kind)
name[0] = unicode.ToLower(name[0])

Expand All @@ -2229,6 +2243,11 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("has the releasePlanAdmission reference", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

name := []rune(releasePlanAdmission.Kind)
name[0] = unicode.ToLower(name[0])

Expand All @@ -2238,6 +2257,11 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("has the releaseServiceConfig reference", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

name := []rune(releaseServiceConfig.Kind)
name[0] = unicode.ToLower(name[0])

Expand All @@ -2247,24 +2271,44 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("has the snapshot reference", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", strings.ToLower(snapshot.Kind))))
Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal",
fmt.Sprintf("%s%c%s", snapshot.Namespace, types.Separator, snapshot.Name))))
})

It("has owner annotations", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.GetAnnotations()[handler.NamespacedNameAnnotation]).To(ContainSubstring(adapter.release.Name))
Expect(pipelineRun.GetAnnotations()[handler.TypeAnnotation]).To(ContainSubstring("Release"))
})

It("has release labels", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.GetLabels()[metadata.PipelinesTypeLabel]).To(Equal(metadata.ManagedPipelineType))
Expect(pipelineRun.GetLabels()[metadata.ReleaseNameLabel]).To(Equal(adapter.release.Name))
Expect(pipelineRun.GetLabels()[metadata.ReleaseNamespaceLabel]).To(Equal(testNamespace))
Expect(pipelineRun.GetLabels()[metadata.ReleaseSnapshotLabel]).To(Equal(adapter.release.Spec.Snapshot))
})

It("references the pipeline specified in the ReleasePlanAdmission", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

var pipelineUrl string
resolverParams := pipelineRun.Spec.PipelineRef.ResolverRef.Params
for i := range resolverParams {
Expand All @@ -2276,6 +2320,11 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("contains a parameter with the taskGitUrl", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", "taskGitUrl")))
var url string
resolverParams := pipelineRun.Spec.PipelineRef.ResolverRef.Params
Expand All @@ -2288,6 +2337,11 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("contains a parameter with the taskGitRevision", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Name", "taskGitRevision")))
var revision string
resolverParams := pipelineRun.Spec.PipelineRef.ResolverRef.Params
Expand All @@ -2300,18 +2354,69 @@ var _ = Describe("Release adapter", Ordered, func() {
})

It("contains the proper timeout value", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Timeouts.Pipeline).To(Equal(releasePlanAdmission.Spec.Pipeline.Timeouts.Pipeline))
})

It("contains a parameter with the verify ec task bundle", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

bundle := enterpriseContractConfigMap.Data["verify_ec_task_bundle"]
Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", Equal(string(bundle)))))
})

It("contains a parameter with the json representation of the EnterpriseContractPolicy", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

jsonSpec, _ := json.Marshal(enterpriseContractPolicy.Spec)
Expect(pipelineRun.Spec.Params).Should(ContainElement(HaveField("Value.StringVal", Equal(string(jsonSpec)))))
})

It("contains a workspace using EmptyDir if there's an override for the pipeline", func() {
pipelineRef, err := releasePlanAdmission.Spec.Pipeline.PipelineRef.GetPipelineGitRef()
Expect(err).To(BeNil())
Expect(pipelineRef).NotTo(BeEmpty())

releaseServiceConfig := &v1alpha1.ReleaseServiceConfig{
Spec: v1alpha1.ReleaseServiceConfigSpec{
VolumeOverrides: map[string]v1alpha1.VolumeType{
pipelineRef: "emptyDir",
},
},
}
releaseServiceConfig.Kind = "ReleaseServiceConfig"
adapter.releaseServiceConfig = releaseServiceConfig

var pipelineRun *tektonv1.PipelineRun
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Workspaces).To(HaveLen(1))
Expect(pipelineRun.Spec.Workspaces[0].EmptyDir).NotTo(BeNil())
Expect(pipelineRun.Spec.Workspaces[0].VolumeClaimTemplate).To(BeNil())
})

It("contains a workspace using EmptyDir if there's not an override for the pipeline", func() {
var err error
pipelineRun, err = adapter.createManagedPipelineRun(resources)
Expect(pipelineRun).NotTo(BeNil())
Expect(err).NotTo(HaveOccurred())

Expect(pipelineRun.Spec.Workspaces).To(HaveLen(1))
Expect(pipelineRun.Spec.Workspaces[0].VolumeClaimTemplate).NotTo(BeNil())
Expect(pipelineRun.Spec.Workspaces[0].EmptyDir).To(BeNil())
})
})

When("createFinalPipelineRun is called", func() {
Expand Down
24 changes: 24 additions & 0 deletions tekton/utils/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,30 @@ func (pr *PipelineRef) GetRevision() (string, error) {
return "", fmt.Errorf("no revision found")
}

// GetPipelineGitRef returns a string containing the url to a Git repo and the path to a Pipeline within that repo.
// If the git resolver is not used or no url and a pathInRepo params are supplied, the function will fail.
func (pr *PipelineRef) GetPipelineGitRef() (string, error) {
if pr.Resolver != "git" {
return "", fmt.Errorf("not a git ref")
}

var url, pathInRepo string
for _, param := range pr.Params {
switch param.Name {
case "url":
url = param.Value
case "pathInRepo":
pathInRepo = param.Value
}
}

if url == "" || pathInRepo == "" {
return "", fmt.Errorf("unexpected git ref params")
}

return fmt.Sprintf("%s/%s", url, pathInRepo), nil
}

// ToTektonPipelineRef converts a PipelineRef object to Tekton's own PipelineRef type and returns it.
func (pr *PipelineRef) ToTektonPipelineRef() *tektonv1.PipelineRef {
params := tektonv1.Params{}
Expand Down
21 changes: 21 additions & 0 deletions tekton/utils/pipeline_run_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,27 @@ func (b *PipelineRunBuilder) WithAnnotations(annotations map[string]string) *Pip
return b
}

// WithEmptyDirVolume creates and adds a workspace backed by EmptyDir and using the provided
// workspace name and volume size.
func (b *PipelineRunBuilder) WithEmptyDirVolume(name, size string) *PipelineRunBuilder {
quantity, err := resource.ParseQuantity(size)
if err != nil {
b.err = multierror.Append(b.err, fmt.Errorf("invalid size format: %v", err))
return b
}

workspace := tektonv1.WorkspaceBinding{
Name: name,
EmptyDir: &corev1.EmptyDirVolumeSource{
SizeLimit: &quantity,
},
}

b.pipelineRun.Spec.Workspaces = append(b.pipelineRun.Spec.Workspaces, workspace)

return b
}

// WithFinalizer adds the given finalizer to the PipelineRun's metadata.
func (b *PipelineRunBuilder) WithFinalizer(finalizer string) *PipelineRunBuilder {
controllerutil.AddFinalizer(b.pipelineRun, finalizer)
Expand Down
Loading

0 comments on commit 46ff4ca

Please sign in to comment.