diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 3c9f697be..df8afc9b4 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -5879,6 +5879,13 @@ "kpack.build.v1alpha2.BuilderSpec": { "type": "object", "properties": { + "additionalLabels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, "order": { "type": "array", "items": { @@ -6108,6 +6115,13 @@ "kpack.build.v1alpha2.ClusterBuilderSpec": { "type": "object", "properties": { + "additionalLabels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, "order": { "type": "array", "items": { @@ -6782,6 +6796,13 @@ "kpack.build.v1alpha2.NamespacedBuilderSpec": { "type": "object", "properties": { + "additionalLabels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, "order": { "type": "array", "items": { @@ -7159,8 +7180,7 @@ "properties": { "lastTransitionTime": { "description": "LastTransitionTime is the last time the condition transitioned from one status to another. We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic differences (all other things held constant).", - "type": "string", - "default": {} + "type": "string" }, "message": { "description": "A human readable message indicating details about the transition.", @@ -7412,7 +7432,6 @@ ], "properties": { "inner": { - "default": {}, "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Time" } } diff --git a/docs/builders.md b/docs/builders.md index c407d0129..abc7e96ff 100644 --- a/docs/builders.md +++ b/docs/builders.md @@ -50,6 +50,8 @@ spec: kind: ClusterBuildpack id: paketo-buildpacks/nodejs version: 1.2.3 + additionalLabels: + custom-label: custom-value ``` * `tag`: The tag to save the builder image. You must have access via the referenced service account. @@ -60,6 +62,7 @@ spec: * `store`: If using ClusterStore, then the reference to the ClusterStore. See the [Resolving Buildpack IDs](#resolving-buildpack-ids) section below. * `name`: The name of the ClusterStore resource in kubernetes. * `kind`: The type as defined in kubernetes. This will always be ClusterStore. +* `additionalLabels`: The custom labels that are desired to be on the Builder/ClusterBuilder images. ### Cluster Builders diff --git a/pkg/apis/build/v1alpha2/builder_types.go b/pkg/apis/build/v1alpha2/builder_types.go index b81ae4023..ffa0edec1 100644 --- a/pkg/apis/build/v1alpha2/builder_types.go +++ b/pkg/apis/build/v1alpha2/builder_types.go @@ -32,7 +32,8 @@ type BuilderSpec struct { Stack corev1.ObjectReference `json:"stack,omitempty"` Store corev1.ObjectReference `json:"store,omitempty"` // +listType - Order []BuilderOrderEntry `json:"order,omitempty"` + Order []BuilderOrderEntry `json:"order,omitempty"` + AdditionalLabels map[string]string `json:"additionalLabels,omitempty"` } // +k8s:openapi-gen=true diff --git a/pkg/cnb/builder_builder.go b/pkg/cnb/builder_builder.go index 43b5c40db..13460ed3e 100644 --- a/pkg/cnb/builder_builder.go +++ b/pkg/cnb/builder_builder.go @@ -51,6 +51,7 @@ type builderBlder struct { runImage string mixins []string os string + additionalLabels map[string]string } func newBuilderBldr(kpackVersion string) *builderBlder { @@ -93,6 +94,10 @@ func (bb *builderBlder) AddGroup(buildpacks ...RemoteBuildpackRef) { bb.order = append(bb.order, corev1alpha1.OrderEntry{Group: group}) } +func (bb *builderBlder) AddAdditionalLabels(additionalLabels map[string]string) { + bb.additionalLabels = additionalLabels +} + func (bb *builderBlder) WriteableImage() (v1.Image, error) { buildpacks := bb.buildpacks() @@ -151,6 +156,11 @@ func (bb *builderBlder) WriteableImage() (v1.Image, error) { return nil, err } + image, err = imagehelpers.SetStringLabels(image, bb.additionalLabels) + if err != nil { + return nil, err + } + return imagehelpers.SetLabels(image, map[string]interface{}{ buildpackOrderLabel: bb.order, buildpackLayersLabel: buildpackLayerMetadata, diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 21c05c197..f3cc8e0de 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -61,6 +61,8 @@ func (r *RemoteBuilderCreator) CreateBuilder(ctx context.Context, builderKeychai builderBldr.AddGroup(buildpacks...) } + builderBldr.AddAdditionalLabels(spec.AdditionalLabels) + writeableImage, err := builderBldr.WriteableImage() if err != nil { return buildapi.BuilderRecord{}, err diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 5fd32923d..78186f401 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -168,6 +168,10 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, }, }, + AdditionalLabels: map[string]string{ + "os": "special", + "importance": "high", + }, } lifecycleProvider = &fakeLifecycleProvider{} @@ -633,6 +637,13 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { } }`, buildpackLayers) + // Assure the loose coupling of the number of labels that should be there + assert.Equal(t, len(clusterBuilderSpec.AdditionalLabels), 2) + for key, value := range clusterBuilderSpec.AdditionalLabels { + additionalLabel, err := imagehelpers.GetStringLabel(savedImage, key) + assert.NoError(t, err) + assert.Equal(t, value, additionalLabel) + } }) it("creates images deterministically ", func() { diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index 341644396..cd0d21938 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -2702,6 +2702,21 @@ func schema_pkg_apis_build_v1alpha2_BuilderSpec(ref common.ReferenceCallback) co }, }, }, + "additionalLabels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, }, }, }, @@ -3122,6 +3137,21 @@ func schema_pkg_apis_build_v1alpha2_ClusterBuilderSpec(ref common.ReferenceCallb }, }, }, + "additionalLabels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, "serviceAccountRef": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, @@ -4411,6 +4441,21 @@ func schema_pkg_apis_build_v1alpha2_NamespacedBuilderSpec(ref common.ReferenceCa }, }, }, + "additionalLabels": { + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, "serviceAccountName": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -5102,7 +5147,6 @@ func schema_pkg_apis_core_v1alpha1_Condition(ref common.ReferenceCallback) commo "lastTransitionTime": { SchemaProps: spec.SchemaProps{ Description: "LastTransitionTime is the last time the condition transitioned from one status to another. We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic differences (all other things held constant).", - Default: map[string]interface{}{}, Type: []string{"string"}, Format: "", }, }, @@ -5550,8 +5594,7 @@ func schema_pkg_apis_core_v1alpha1_VolatileTime(ref common.ReferenceCallback) co Properties: map[string]spec.Schema{ "inner": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, },