diff --git a/api/v1alpha1/buildjob_types.go b/api/v1alpha1/buildjob_types.go index 2004793..762aff2 100644 --- a/api/v1alpha1/buildjob_types.go +++ b/api/v1alpha1/buildjob_types.go @@ -15,6 +15,7 @@ package v1alpha1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -143,6 +144,12 @@ type BuildJobSpec struct { Caches []CacheMount `json:"caches,omitempty"` // +optional Timeout *metav1.Duration `json:"timeout,omitempty"` + + // WorkspaceSize is the storage size of the PVC created for the build workspace. + // Defaults to 10Gi when not specified. Use larger values for AAOS/AOSP builds. + // +optional + // +kubebuilder:default="10Gi" + WorkspaceSize *resource.Quantity `json:"workspaceSize,omitempty"` } type NamedStage struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 906061b..80e36d6 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -149,6 +149,10 @@ func (in *BuildJobSpec) DeepCopyInto(out *BuildJobSpec) { *out = new(v1.Duration) **out = **in } + if in.WorkspaceSize != nil { + x := in.WorkspaceSize.DeepCopy() + out.WorkspaceSize = &x + } } func (in *BuildJobSpec) DeepCopy() *BuildJobSpec { if in == nil { diff --git a/config/crd/bases/builder.sdv.cloud.redhat.com_buildjobs.yaml b/config/crd/bases/builder.sdv.cloud.redhat.com_buildjobs.yaml index 5d00292..bc3ed50 100644 --- a/config/crd/bases/builder.sdv.cloud.redhat.com_buildjobs.yaml +++ b/config/crd/bases/builder.sdv.cloud.redhat.com_buildjobs.yaml @@ -150,6 +150,12 @@ spec: mountPath: { type: string } timeout: type: string + workspaceSize: + type: string + default: "10Gi" + description: >- + Storage size of the PVC created for the build workspace. + Defaults to 10Gi. Use larger values for AAOS/AOSP builds (e.g. 500Gi). status: type: object properties: diff --git a/internal/tekton/pipelinerun_builder.go b/internal/tekton/pipelinerun_builder.go index 60b8201..70547e0 100644 --- a/internal/tekton/pipelinerun_builder.go +++ b/internal/tekton/pipelinerun_builder.go @@ -177,6 +177,10 @@ if r.status >= 400: pipelineWorkspaces := []interface{}{ map[string]interface{}{"name": workspaceName}, } + workspaceSize := "10Gi" + if bj.Spec.WorkspaceSize != nil { + workspaceSize = bj.Spec.WorkspaceSize.String() + } runWorkspaces := []interface{}{ map[string]interface{}{ "name": workspaceName, @@ -185,7 +189,7 @@ if r.status >= 400: "accessModes": []interface{}{"ReadWriteOnce"}, "resources": map[string]interface{}{ "requests": map[string]interface{}{ - "storage": "10Gi", + "storage": workspaceSize, }, }, }, @@ -442,11 +446,11 @@ func buildOCIArtifactTask(bj *buildv1alpha1.BuildJob, envVars []interface{}, run ref := fmt.Sprintf("%s:%s", oci.Repository, tag) annotations := map[string]string{ - "org.opencontainers.image.title": bj.Name, - "vnd.auto.target.board": bj.Spec.Target.Board, - "vnd.auto.target.platform": bj.Spec.Target.Platform, - "vnd.auto.target.architecture": bj.Spec.Target.Architecture, - "vnd.auto.build.generation": fmt.Sprintf("%d", bj.Generation), + "org.opencontainers.image.title": bj.Name, + "vnd.auto.target.board": bj.Spec.Target.Board, + "vnd.auto.target.platform": bj.Spec.Target.Platform, + "vnd.auto.target.architecture": bj.Spec.Target.Architecture, + "vnd.auto.build.generation": fmt.Sprintf("%d", bj.Generation), } if bj.Spec.Target.Variant != "" { annotations["vnd.auto.target.variant"] = bj.Spec.Target.Variant diff --git a/internal/tekton/pipelinerun_builder_test.go b/internal/tekton/pipelinerun_builder_test.go index ba1d98a..621ada7 100644 --- a/internal/tekton/pipelinerun_builder_test.go +++ b/internal/tekton/pipelinerun_builder_test.go @@ -20,6 +20,7 @@ import ( "time" buildv1alpha1 "github.com/centos-automotive-suite/bob/api/v1alpha1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -867,6 +868,48 @@ func TestBuildPipelineRun_PVCArtifactStillWorks(t *testing.T) { } } +// TestBuildPipelineRun_WorkspaceSize_Custom verifies that spec.workspaceSize is +// propagated to the PVC volumeClaimTemplate (regression test for issue #29). +func TestBuildPipelineRun_WorkspaceSize_Custom(t *testing.T) { + bj := newTestBuildJob() + q := resource.MustParse("500Gi") + bj.Spec.WorkspaceSize = &q + pr := BuildPipelineRun(bj) + + storage := getWorkspaceStorage(t, pr) + if storage != "500Gi" { + t.Fatalf("expected workspace storage 500Gi, got %q", storage) + } +} + +// TestBuildPipelineRun_WorkspaceSize_DefaultWhenNil verifies the 10Gi default +// is used when spec.workspaceSize is nil (regression test for issue #29). +func TestBuildPipelineRun_WorkspaceSize_DefaultWhenNil(t *testing.T) { + bj := newTestBuildJob() + bj.Spec.WorkspaceSize = nil + pr := BuildPipelineRun(bj) + + storage := getWorkspaceStorage(t, pr) + if storage != "10Gi" { + t.Fatalf("expected default workspace storage 10Gi, got %q", storage) + } +} + +func getWorkspaceStorage(t *testing.T, pr *unstructured.Unstructured) string { + t.Helper() + spec := pr.Object["spec"].(map[string]interface{}) + workspaces := spec["workspaces"].([]interface{}) + for _, ws := range workspaces { + wsMap := ws.(map[string]interface{}) + storage, _, _ := unstructured.NestedString(wsMap, "volumeClaimTemplate", "spec", "resources", "requests", "storage") + if storage != "" { + return storage + } + } + t.Fatal("storage not found in any workspace volumeClaimTemplate") + return "" +} + func getPipelineTasks(t *testing.T, pr *unstructured.Unstructured) []interface{} { t.Helper() spec := pr.Object["spec"].(map[string]interface{})