diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 9334b6529..3ae8ccd54 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": { @@ -6115,6 +6122,13 @@ "kpack.build.v1alpha2.ClusterBuilderSpec": { "type": "object", "properties": { + "additionalLabels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, "order": { "type": "array", "items": { @@ -6804,6 +6818,13 @@ "kpack.build.v1alpha2.NamespacedBuilderSpec": { "type": "object", "properties": { + "additionalLabels": { + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, "order": { "type": "array", "items": { @@ -7181,8 +7202,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.", @@ -7440,7 +7460,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 47cca0071..4cf4d2d6d 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 343d5966f..48bccde5c 100644 --- a/pkg/apis/build/v1alpha2/builder_types.go +++ b/pkg/apis/build/v1alpha2/builder_types.go @@ -36,7 +36,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 f225d2d2e..20e978c22 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -65,6 +65,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 a35d16e4d..40e8e7a5e 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{} @@ -639,6 +643,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 c585658c9..84c8d8f76 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -2703,6 +2703,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: "", + }, + }, + }, + }, + }, }, }, }, @@ -3136,6 +3151,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{}{}, @@ -4447,6 +4477,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"}, @@ -5138,7 +5183,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: "", }, }, @@ -5598,8 +5642,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"), }, }, },