diff --git a/API.md b/API.md
index 98cfafa..c32d32f 100644
--- a/API.md
+++ b/API.md
@@ -1216,6 +1216,36 @@ public readonly key: string;
---
+### PersistentVolumeClaimRef
+
+A reference to a PersistentVolumeClaim.
+
+#### Initializer
+
+```typescript
+import { PersistentVolumeClaimRef } from 'cdk8s-pipelines'
+
+const persistentVolumeClaimRef: PersistentVolumeClaimRef = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| claimName
| string
| *No description.* |
+
+---
+
+##### `claimName`Required
+
+```typescript
+public readonly claimName: string;
+```
+
+- *Type:* string
+
+---
+
### PipelineParam
A Pipeline parameter.
@@ -1462,6 +1492,7 @@ const pipelineRunSpec: PipelineRunSpec = { ... }
| --- | --- | --- |
| pipelineRef
| PipelineRef
| Required `Pipeline` reference. |
| params
| PipelineRunParam[]
| *No description.* |
+| workspaces
| PipelineRunWorkspace[]
| *No description.* |
---
@@ -1487,6 +1518,70 @@ public readonly params: PipelineRunParam[];
---
+##### `workspaces`Optional
+
+```typescript
+public readonly workspaces: PipelineRunWorkspace[];
+```
+
+- *Type:* PipelineRunWorkspace[]
+
+---
+
+### PipelineRunWorkspace
+
+The `Workspace` configuration for a `PipelineRun`.
+
+> [https://tekton.dev/docs/pipelines/pipelineruns/#specifying-workspaces](https://tekton.dev/docs/pipelines/pipelineruns/#specifying-workspaces)
+
+#### Initializer
+
+```typescript
+import { PipelineRunWorkspace } from 'cdk8s-pipelines'
+
+const pipelineRunWorkspace: PipelineRunWorkspace = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| name
| string
| *No description.* |
+| persistentVolumeClaim
| PersistentVolumeClaimRef
| *No description.* |
+| subPath
| string
| *No description.* |
+
+---
+
+##### `name`Optional
+
+```typescript
+public readonly name: string;
+```
+
+- *Type:* string
+
+---
+
+##### `persistentVolumeClaim`Required
+
+```typescript
+public readonly persistentVolumeClaim: PersistentVolumeClaimRef;
+```
+
+- *Type:* PersistentVolumeClaimRef
+
+---
+
+##### `subPath`Required
+
+```typescript
+public readonly subPath: string;
+```
+
+- *Type:* string
+
+---
+
### PipelineSpec
The `spec` part of the Pipeline.
@@ -2361,6 +2456,8 @@ public readonly logicalID: string;
### ParameterBuilder
+Builds the parameters for use by Tasks and Pipelines.
+
#### Initializers
```typescript
@@ -2492,7 +2589,7 @@ Sets the value for the parameter.
| description
| string
| *No description.* |
| requiresPipelineParameter
| boolean
| Returns true if this parameter expects input at the pipeline level. |
| defaultValue
| string
| *No description.* |
-| logicalID
| string
| *No description.* |
+| logicalID
| string
| Gets the logicalID for the `ParameterBuilder`, which is used by the underlying construct. |
| name
| string
| *No description.* |
| type
| string
| Gets the type of the parameter. |
| value
| string
| Gets the value of the parameter. |
@@ -2539,6 +2636,8 @@ public readonly logicalID: string;
- *Type:* string
+Gets the logicalID for the `ParameterBuilder`, which is used by the underlying construct.
+
---
##### `name`Optional
@@ -2676,7 +2775,8 @@ public withTask(taskB: TaskBuilder): PipelineBuilder
| **Name** | **Type** | **Description** |
| --- | --- | --- |
| name
| string
| Gets the name of the pipeline. |
-| params
| PipelineParam[]
| Returns the array of `PipelineParam` objects. |
+| params
| PipelineParam[]
| Returns the array of `PipelineParam` objects that represent the parameters configured for the `Pipeline`. |
+| workspaces
| PipelineWorkspace[]
| Returns the array of `PipelineWorkspace` objects that represent the workspaces configured for the `Pipeline`. |
---
@@ -2700,7 +2800,7 @@ public readonly params: PipelineParam[];
- *Type:* PipelineParam[]
-Returns the array of `PipelineParam` objects.
+Returns the array of `PipelineParam` objects that represent the parameters configured for the `Pipeline`.
Note this is an "expensive" get because it loops through the tasks in the
pipeline and checks for duplicates in the pipeline parameters for each task
@@ -2709,6 +2809,23 @@ a local variable before the loop and reference that instead.
---
+##### `workspaces`Required
+
+```typescript
+public readonly workspaces: PipelineWorkspace[];
+```
+
+- *Type:* PipelineWorkspace[]
+
+Returns the array of `PipelineWorkspace` objects that represent the workspaces configured for the `Pipeline`.
+
+This is an "expensive" get because it loops through the workspaces in the
+pipeline and checks for duplicates in the pipeline workspaces for each task
+workspace found. You should avoid calling this in a loop--instead, declare
+a local variable before the loop and reference that instead.
+
+---
+
### PipelineRunBuilder
@@ -2764,6 +2881,7 @@ The `Pipeline` for which to create this run, using the `PipelineBuilder`.
| withClusterRoleBindingProps
| *No description.* |
| withRunParam
| Adds a run parameter to the `PipelineRun`. |
| withServiceAccount
| Uses the provided role name for the `serviceAccountName` on the `PipelineRun`. |
+| withWorkspace
| Allows you to specify the name of a `PersistentVolumeClaim` but does not do any compile-time validation on the volume claim's name or existence. |
---
@@ -2839,6 +2957,40 @@ The name of the service account (`serviceAccountName`) to use.
---
+##### `withWorkspace`
+
+```typescript
+public withWorkspace(name: string, claimName: string, subPath: string): PipelineRunBuilder
+```
+
+Allows you to specify the name of a `PersistentVolumeClaim` but does not do any compile-time validation on the volume claim's name or existence.
+
+> [https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim)
+
+###### `name`Required
+
+- *Type:* string
+
+The name of the workspace in the `PipelineRun` that will be used by the `Pipeline`.
+
+---
+
+###### `claimName`Required
+
+- *Type:* string
+
+The name of the `PersistentVolumeClaim` to use for the `workspace`.
+
+---
+
+###### `subPath`Required
+
+- *Type:* string
+
+The sub path on the `persistentVolumeClaim` to use for the `workspace`.
+
+---
+
diff --git a/src/builders.ts b/src/builders.ts
index 1dad501..4586cb7 100644
--- a/src/builders.ts
+++ b/src/builders.ts
@@ -7,7 +7,16 @@ import * as fs from 'fs';
import { ApiObject, ApiObjectProps, Yaml } from 'cdk8s';
import { Construct } from 'constructs';
import { buildParam } from './common';
-import { Pipeline, PipelineParam, PipelineRun, PipelineRunParam, PipelineTask, PipelineTaskWorkspace, PipelineWorkspace } from './pipelines';
+import {
+ Pipeline,
+ PipelineParam,
+ PipelineRun,
+ PipelineRunParam,
+ PipelineRunWorkspace,
+ PipelineTask,
+ PipelineTaskWorkspace,
+ PipelineWorkspace,
+} from './pipelines';
import { Task, TaskEnvValueSource, TaskParam, TaskProps, TaskSpecParam, TaskStep, TaskStepEnv, TaskWorkspace } from './tasks';
const DefaultPipelineServiceAccountName = 'default:pipeline';
@@ -118,6 +127,9 @@ export class WorkspaceBuilder {
}
+/**
+ * Builds the parameters for use by Tasks and Pipelines.
+ */
export class ParameterBuilder {
private readonly _logicalID: string;
private _name?: string;
@@ -132,6 +144,10 @@ export class ParameterBuilder {
this._requiresPipelineParam = false;
}
+ /**
+ * Gets the logicalID for the `ParameterBuilder`, which is used by the underlying
+ * construct.
+ */
public get logicalID(): string | undefined {
return this._logicalID;
}
@@ -687,7 +703,8 @@ export class PipelineBuilder {
}
/**
- * Returns the array of `PipelineParam` objects.
+ * Returns the array of `PipelineParam` objects that represent the parameters
+ * configured for the `Pipeline`.
*
* Note this is an "expensive" get because it loops through the tasks in the
* pipeline and checks for duplicates in the pipeline parameters for each task
@@ -718,6 +735,35 @@ export class PipelineBuilder {
return Array.from(pipelineParams.values());
}
+ /**
+ * Returns the array of `PipelineWorkspace` objects that represent the workspaces
+ * configured for the `Pipeline`.
+ *
+ * This is an "expensive" get because it loops through the workspaces in the
+ * pipeline and checks for duplicates in the pipeline workspaces for each task
+ * workspace found. You should avoid calling this in a loop--instead, declare
+ * a local variable before the loop and reference that instead.
+ *
+ * @returns PipelineWorkspace[] An array of the pipeline workspaces.
+ */
+ public get workspaces(): PipelineWorkspace[] {
+ const pipelineWorkspaces = new Map();
+ this._tasks?.forEach((t) => {
+ t.workspaces?.forEach((w) => {
+ // Only add the workspace on the pipeline level if it is not already
+ // there...
+ const ws = pipelineWorkspaces.get(w.name!);
+ if (!ws) {
+ pipelineWorkspaces.set(w.name!, {
+ name: w.name,
+ description: w.description,
+ });
+ }
+ });
+ });
+ return Array.from(pipelineWorkspaces.values());
+ }
+
/**
* Builds the actual [Pipeline](https://tekton.dev/docs/getting-started/pipelines/)
* from the settings configured using the fluid syntax.
@@ -725,8 +771,6 @@ export class PipelineBuilder {
public buildPipeline(opts: BuilderOptions = DefaultBuilderOptions): void {
// TODO: validate the object
- const pipelineParams = new Map();
- const pipelineWorkspaces = new Map();
const pipelineTasks: PipelineTask[] = new Array();
// For making a list to make sure that tasks aren't duplicated when doing
// the build. Not that it really hurts anything, but it makes the multidoc
@@ -739,17 +783,6 @@ export class PipelineBuilder {
const taskWorkspaces: PipelineTaskWorkspace[] = new Array();
t.parameters?.forEach(p => {
- const pp = pipelineParams.get(p.name!);
- if (!pp) {
- // Do not add it to the pipeline if there is no need to add it...
- if (p.requiresPipelineParameter) {
- pipelineParams.set(p.name!, {
- name: p.name,
- type: p.type,
- });
- }
- }
-
taskParams.push({
name: p.logicalID,
value: p.value,
@@ -757,16 +790,6 @@ export class PipelineBuilder {
});
t.workspaces?.forEach((w) => {
- // Only add the workspace on the pipeline level if it is not already
- // there...
- const ws = pipelineWorkspaces.get(w.name!);
- if (!ws) {
- pipelineWorkspaces.set(w.name!, {
- name: w.name,
- description: w.description,
- });
- }
-
taskWorkspaces.push({
name: w.logicalID,
workspace: w.name,
@@ -797,8 +820,8 @@ export class PipelineBuilder {
},
spec: {
description: this._description,
- params: Array.from(pipelineParams.values()),
- workspaces: Array.from(pipelineWorkspaces.values()),
+ params: this.params,
+ workspaces: this.workspaces,
tasks: pipelineTasks,
},
});
@@ -837,6 +860,7 @@ export class PipelineRunBuilder {
private readonly _id: string;
private readonly _pipeline: PipelineBuilder;
private readonly _runParams: PipelineRunParam[];
+ private readonly _runWorkspaces: PipelineRunWorkspace[];
private _sa: string;
private _crbProps: ApiObjectProps;
@@ -859,6 +883,7 @@ export class PipelineRunBuilder {
this._sa = DefaultPipelineServiceAccountName;
this._crbProps = DefaultClusterRoleBindingProps;
this._runParams = new Array();
+ this._runWorkspaces = new Array();
}
/**
@@ -882,6 +907,27 @@ export class PipelineRunBuilder {
return this;
}
+ /**
+ * Allows you to specify the name of a `PersistentVolumeClaim` but does not
+ * do any compile-time validation on the volume claim's name or existence.
+ *
+ * @see https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim
+ *
+ * @param name The name of the workspace in the `PipelineRun` that will be used by the `Pipeline`.
+ * @param claimName The name of the `PersistentVolumeClaim` to use for the `workspace`.
+ * @param subPath The sub path on the `persistentVolumeClaim` to use for the `workspace`.
+ */
+ public withWorkspace(name: string, claimName: string, subPath: string): PipelineRunBuilder {
+ this._runWorkspaces.push({
+ name: name,
+ persistentVolumeClaim: {
+ claimName: claimName,
+ },
+ subPath: subPath,
+ });
+ return this;
+ }
+
public withClusterRoleBindingProps(props: ApiObjectProps): PipelineRunBuilder {
this._crbProps = props;
return this;
@@ -919,6 +965,16 @@ export class PipelineRunBuilder {
}
});
+ // Do the same thing for workspaces. Check to make sure that the workspaces
+ // expected by the Pipeline are defined in the PipelineRun.
+ const workspaces: PipelineWorkspace[] = this._pipeline.workspaces;
+ workspaces.forEach((ws) => {
+ const pws = this._runWorkspaces.find((obj) => obj.name == ws.name);
+ if (! pws) {
+ throw new Error(`Pipeline workspace '${ws.name}' is not defined in PipelineRun '${this._id}'`);
+ }
+ });
+
new PipelineRun(this._scope, this._id, {
metadata: {
name: this._id,
diff --git a/src/pipelines.ts b/src/pipelines.ts
index 637e042..473cfc6 100644
--- a/src/pipelines.ts
+++ b/src/pipelines.ts
@@ -172,6 +172,23 @@ export interface PipelineRunParam extends NamedResource {
readonly value: string;
}
+/**
+ * A reference to a PersistentVolumeClaim
+ */
+export interface PersistentVolumeClaimRef {
+ readonly claimName: string;
+}
+
+/**
+ * The `Workspace` configuration for a `PipelineRun`.
+ *
+ * @see https://tekton.dev/docs/pipelines/pipelineruns/#specifying-workspaces
+ */
+export interface PipelineRunWorkspace extends NamedResource {
+ readonly persistentVolumeClaim: PersistentVolumeClaimRef;
+ readonly subPath: string;
+}
+
/**
* The details for the `PipelineRun`.
* @see https://tekton.dev/docs/pipelines/pipelineruns/#configuring-a-pipelinerun
@@ -182,6 +199,7 @@ export interface PipelineRunSpec {
*/
readonly pipelineRef: PipelineRef;
readonly params?: PipelineRunParam[];
+ readonly workspaces?: PipelineRunWorkspace[];
}
export interface PipelineRunProps {
diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts
index fc5f74e..4e3bdb5 100644
--- a/test/pipelinebuilder.test.ts
+++ b/test/pipelinebuilder.test.ts
@@ -11,6 +11,30 @@ import {
} from '../src';
class PipelineRunTest extends Chart {
+ constructor(scope: Construct, id: string, props?: ChartProps) {
+ super(scope, id, props);
+
+ const myTask = new TaskBuilder(this, 'fetch-source')
+ .withName('git-clone')
+ .withWorkspace(new WorkspaceBuilder('output')
+ .withName('shared-data')
+ .withDescription('The files cloned by the task'))
+ .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', ''));
+
+ const pipeline = new PipelineBuilder(this, 'my-pipeline')
+ .withName('clone-build-push')
+ .withDescription('This pipeline closes a repository, builds a Docker image, etc.')
+ .withTask(myTask);
+ pipeline.buildPipeline({ includeDependencies: true });
+
+ new PipelineRunBuilder(this, 'my-pipeline-run', pipeline)
+ .withRunParam('repo-url', 'https://github.com/exmaple/my-repo')
+ .withWorkspace('shared-data', 'dataPVC', 'my-shared-data')
+ .buildPipelineRun({ includeDependencies: true });
+ }
+}
+
+class PipelineRunTestWithUndefinedWorkspaceError extends Chart {
constructor(scope: Construct, id: string, props?: ChartProps) {
super(scope, id, props);
@@ -215,6 +239,14 @@ describe('PipelineBuilderTest', () => {
expect(f).toThrowError('PipelineRun parameter \'theundefinedparam\' does not exist in pipeline \'clone-build-push\'');
});
+ test('PipelineRunBuilderWithWorkspaceError', () => {
+ const app = Testing.app();
+ const f = () => {
+ new PipelineRunTestWithUndefinedWorkspaceError(app, 'test-chart');
+ };
+ expect(f).toThrowError('Pipeline workspace \'shared-data\' is not defined in PipelineRun \'my-pipeline-run\'');
+ });
+
test('PipelineBuilderWithComplexTasks', () => {
const app = Testing.app();
const chart = new MySecondTestChart(app, 'my-second-test-chart');