From e693700f02ac8aaf56dd5db1a70fcee5411fc820 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 7 May 2024 15:11:43 -0400 Subject: [PATCH 01/39] Add (preliminary) ClusterLifecycle types Signed-off-by: Natalie Arellano --- .../build/v1alpha2/cluster_lifecycle_types.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/apis/build/v1alpha2/cluster_lifecycle_types.go diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go new file mode 100644 index 000000000..7a4971e43 --- /dev/null +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -0,0 +1,63 @@ +package v1alpha2 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" +) + +const ( + ClusterLifecycleKind = "ClusterLifecycle" + ClusterLifecycleCRName = "clusterlifecycles.kpack.io" +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object,k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMetaAccessor + +// +k8s:openapi-gen=true +type ClusterLifecycle struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterLifecycleSpec `json:"spec"` + Status ClusterLifecycleStatus `json:"status"` +} + +// +k8s:openapi-gen=true +type ClusterLifecycleSpec struct { + // +listType + corev1alpha1.ImageSource `json:",inline"` + ServiceAccountRef *corev1.ObjectReference `json:"serviceAccountRef,omitempty"` +} + +// +k8s:openapi-gen=true +type ClusterLifecycleStatus struct { + corev1alpha1.Status `json:",inline"` + ResolvedClusterLifecycle `json:",inline"` +} + +// +k8s:openapi-gen=true +type ResolvedClusterLifecycle struct { + // Id string `json:"id,omitempty"` + Version string `json:"version,omitempty"` + BuildpackAPIs []string `json:"buildpackAPIs,omitempty"` + PlatformAPIs []string `json:"platformAPIs,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// +k8s:openapi-gen=true +type ClusterLifecycleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + // +k8s:listType=atomic + Items []ClusterLifecycle `json:"items"` +} + +func (*ClusterLifecycle) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(ClusterLifecycleKind) +} From e087af515ddedc9557de84fc45a8050cc09af5aa Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 7 May 2024 15:11:58 -0400 Subject: [PATCH 02/39] Run hack/update-codegen.sh Signed-off-by: Natalie Arellano --- .../build/v1alpha2/zz_generated.deepcopy.go | 142 ++++++++++++++ .../typed/build/v1alpha2/build_client.go | 5 + .../typed/build/v1alpha2/clusterlifecycle.go | 184 ++++++++++++++++++ .../build/v1alpha2/fake/fake_build_client.go | 4 + .../v1alpha2/fake/fake_clusterlifecycle.go | 133 +++++++++++++ .../build/v1alpha2/generated_expansion.go | 2 + .../build/v1alpha2/clusterlifecycle.go | 89 +++++++++ .../build/v1alpha2/interface.go | 7 + .../informers/externalversions/generic.go | 2 + .../build/v1alpha2/clusterlifecycle.go | 68 +++++++ .../build/v1alpha2/expansion_generated.go | 4 + 11 files changed, 640 insertions(+) create mode 100644 pkg/client/clientset/versioned/typed/build/v1alpha2/clusterlifecycle.go create mode 100644 pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_clusterlifecycle.go create mode 100644 pkg/client/informers/externalversions/build/v1alpha2/clusterlifecycle.go create mode 100644 pkg/client/listers/build/v1alpha2/clusterlifecycle.go diff --git a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go index 1411d8cf9..089b52226 100644 --- a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go @@ -498,6 +498,13 @@ func (in *BuilderSpec) DeepCopyInto(out *BuilderSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AdditionalLabels != nil { + in, out := &in.AdditionalLabels, &out.AdditionalLabels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } return } @@ -858,6 +865,115 @@ func (in *ClusterBuildpackStatus) DeepCopy() *ClusterBuildpackStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterLifecycle) DeepCopyInto(out *ClusterLifecycle) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterLifecycle. +func (in *ClusterLifecycle) DeepCopy() *ClusterLifecycle { + if in == nil { + return nil + } + out := new(ClusterLifecycle) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObjectMetaAccessor is an autogenerated deepcopy function, copying the receiver, creating a new metav1.ObjectMetaAccessor. +func (in *ClusterLifecycle) DeepCopyObjectMetaAccessor() metav1.ObjectMetaAccessor { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterLifecycle) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterLifecycleList) DeepCopyInto(out *ClusterLifecycleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterLifecycle, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterLifecycleList. +func (in *ClusterLifecycleList) DeepCopy() *ClusterLifecycleList { + if in == nil { + return nil + } + out := new(ClusterLifecycleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterLifecycleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterLifecycleSpec) DeepCopyInto(out *ClusterLifecycleSpec) { + *out = *in + out.ImageSource = in.ImageSource + if in.ServiceAccountRef != nil { + in, out := &in.ServiceAccountRef, &out.ServiceAccountRef + *out = new(v1.ObjectReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterLifecycleSpec. +func (in *ClusterLifecycleSpec) DeepCopy() *ClusterLifecycleSpec { + if in == nil { + return nil + } + out := new(ClusterLifecycleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterLifecycleStatus) DeepCopyInto(out *ClusterLifecycleStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.ResolvedClusterLifecycle.DeepCopyInto(&out.ResolvedClusterLifecycle) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterLifecycleStatus. +func (in *ClusterLifecycleStatus) DeepCopy() *ClusterLifecycleStatus { + if in == nil { + return nil + } + out := new(ClusterLifecycleStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterStack) DeepCopyInto(out *ClusterStack) { *out = *in @@ -1482,6 +1598,32 @@ func (in *RegistryCache) DeepCopy() *RegistryCache { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResolvedClusterLifecycle) DeepCopyInto(out *ResolvedClusterLifecycle) { + *out = *in + if in.BuildpackAPIs != nil { + in, out := &in.BuildpackAPIs, &out.BuildpackAPIs + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PlatformAPIs != nil { + in, out := &in.PlatformAPIs, &out.PlatformAPIs + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResolvedClusterLifecycle. +func (in *ResolvedClusterLifecycle) DeepCopy() *ResolvedClusterLifecycle { + if in == nil { + return nil + } + out := new(ResolvedClusterLifecycle) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResolvedClusterStack) DeepCopyInto(out *ResolvedClusterStack) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/build/v1alpha2/build_client.go b/pkg/client/clientset/versioned/typed/build/v1alpha2/build_client.go index 4cc70b2c6..e3ce9fb97 100644 --- a/pkg/client/clientset/versioned/typed/build/v1alpha2/build_client.go +++ b/pkg/client/clientset/versioned/typed/build/v1alpha2/build_client.go @@ -33,6 +33,7 @@ type KpackV1alpha2Interface interface { BuildpacksGetter ClusterBuildersGetter ClusterBuildpacksGetter + ClusterLifecyclesGetter ClusterStacksGetter ClusterStoresGetter ImagesGetter @@ -64,6 +65,10 @@ func (c *KpackV1alpha2Client) ClusterBuildpacks() ClusterBuildpackInterface { return newClusterBuildpacks(c) } +func (c *KpackV1alpha2Client) ClusterLifecycles() ClusterLifecycleInterface { + return newClusterLifecycles(c) +} + func (c *KpackV1alpha2Client) ClusterStacks() ClusterStackInterface { return newClusterStacks(c) } diff --git a/pkg/client/clientset/versioned/typed/build/v1alpha2/clusterlifecycle.go b/pkg/client/clientset/versioned/typed/build/v1alpha2/clusterlifecycle.go new file mode 100644 index 000000000..be82d7589 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/build/v1alpha2/clusterlifecycle.go @@ -0,0 +1,184 @@ +/* + * Copyright 2019 The original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + "time" + + v1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + scheme "github.com/pivotal/kpack/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterLifecyclesGetter has a method to return a ClusterLifecycleInterface. +// A group's client should implement this interface. +type ClusterLifecyclesGetter interface { + ClusterLifecycles() ClusterLifecycleInterface +} + +// ClusterLifecycleInterface has methods to work with ClusterLifecycle resources. +type ClusterLifecycleInterface interface { + Create(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.CreateOptions) (*v1alpha2.ClusterLifecycle, error) + Update(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (*v1alpha2.ClusterLifecycle, error) + UpdateStatus(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (*v1alpha2.ClusterLifecycle, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha2.ClusterLifecycle, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.ClusterLifecycleList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ClusterLifecycle, err error) + ClusterLifecycleExpansion +} + +// clusterLifecycles implements ClusterLifecycleInterface +type clusterLifecycles struct { + client rest.Interface +} + +// newClusterLifecycles returns a ClusterLifecycles +func newClusterLifecycles(c *KpackV1alpha2Client) *clusterLifecycles { + return &clusterLifecycles{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterLifecycle, and returns the corresponding clusterLifecycle object, and an error if there is any. +func (c *clusterLifecycles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.ClusterLifecycle, err error) { + result = &v1alpha2.ClusterLifecycle{} + err = c.client.Get(). + Resource("clusterlifecycles"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterLifecycles that match those selectors. +func (c *clusterLifecycles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.ClusterLifecycleList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.ClusterLifecycleList{} + err = c.client.Get(). + Resource("clusterlifecycles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterLifecycles. +func (c *clusterLifecycles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Resource("clusterlifecycles"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a clusterLifecycle and creates it. Returns the server's representation of the clusterLifecycle, and an error, if there is any. +func (c *clusterLifecycles) Create(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.CreateOptions) (result *v1alpha2.ClusterLifecycle, err error) { + result = &v1alpha2.ClusterLifecycle{} + err = c.client.Post(). + Resource("clusterlifecycles"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterLifecycle). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a clusterLifecycle and updates it. Returns the server's representation of the clusterLifecycle, and an error, if there is any. +func (c *clusterLifecycles) Update(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (result *v1alpha2.ClusterLifecycle, err error) { + result = &v1alpha2.ClusterLifecycle{} + err = c.client.Put(). + Resource("clusterlifecycles"). + Name(clusterLifecycle.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterLifecycle). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *clusterLifecycles) UpdateStatus(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (result *v1alpha2.ClusterLifecycle, err error) { + result = &v1alpha2.ClusterLifecycle{} + err = c.client.Put(). + Resource("clusterlifecycles"). + Name(clusterLifecycle.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(clusterLifecycle). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the clusterLifecycle and deletes it. Returns an error if one occurs. +func (c *clusterLifecycles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusterlifecycles"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterLifecycles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Resource("clusterlifecycles"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched clusterLifecycle. +func (c *clusterLifecycles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ClusterLifecycle, err error) { + result = &v1alpha2.ClusterLifecycle{} + err = c.client.Patch(pt). + Resource("clusterlifecycles"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_build_client.go b/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_build_client.go index 6e91f1358..8c610335b 100644 --- a/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_build_client.go +++ b/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_build_client.go @@ -48,6 +48,10 @@ func (c *FakeKpackV1alpha2) ClusterBuildpacks() v1alpha2.ClusterBuildpackInterfa return &FakeClusterBuildpacks{c} } +func (c *FakeKpackV1alpha2) ClusterLifecycles() v1alpha2.ClusterLifecycleInterface { + return &FakeClusterLifecycles{c} +} + func (c *FakeKpackV1alpha2) ClusterStacks() v1alpha2.ClusterStackInterface { return &FakeClusterStacks{c} } diff --git a/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_clusterlifecycle.go b/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_clusterlifecycle.go new file mode 100644 index 000000000..0a5e79cf3 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/build/v1alpha2/fake/fake_clusterlifecycle.go @@ -0,0 +1,133 @@ +/* + * Copyright 2019 The original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeClusterLifecycles implements ClusterLifecycleInterface +type FakeClusterLifecycles struct { + Fake *FakeKpackV1alpha2 +} + +var clusterlifecyclesResource = schema.GroupVersionResource{Group: "kpack.io", Version: "v1alpha2", Resource: "clusterlifecycles"} + +var clusterlifecyclesKind = schema.GroupVersionKind{Group: "kpack.io", Version: "v1alpha2", Kind: "ClusterLifecycle"} + +// Get takes name of the clusterLifecycle, and returns the corresponding clusterLifecycle object, and an error if there is any. +func (c *FakeClusterLifecycles) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.ClusterLifecycle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(clusterlifecyclesResource, name), &v1alpha2.ClusterLifecycle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ClusterLifecycle), err +} + +// List takes label and field selectors, and returns the list of ClusterLifecycles that match those selectors. +func (c *FakeClusterLifecycles) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.ClusterLifecycleList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(clusterlifecyclesResource, clusterlifecyclesKind, opts), &v1alpha2.ClusterLifecycleList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.ClusterLifecycleList{ListMeta: obj.(*v1alpha2.ClusterLifecycleList).ListMeta} + for _, item := range obj.(*v1alpha2.ClusterLifecycleList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested clusterLifecycles. +func (c *FakeClusterLifecycles) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(clusterlifecyclesResource, opts)) +} + +// Create takes the representation of a clusterLifecycle and creates it. Returns the server's representation of the clusterLifecycle, and an error, if there is any. +func (c *FakeClusterLifecycles) Create(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.CreateOptions) (result *v1alpha2.ClusterLifecycle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(clusterlifecyclesResource, clusterLifecycle), &v1alpha2.ClusterLifecycle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ClusterLifecycle), err +} + +// Update takes the representation of a clusterLifecycle and updates it. Returns the server's representation of the clusterLifecycle, and an error, if there is any. +func (c *FakeClusterLifecycles) Update(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (result *v1alpha2.ClusterLifecycle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(clusterlifecyclesResource, clusterLifecycle), &v1alpha2.ClusterLifecycle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ClusterLifecycle), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeClusterLifecycles) UpdateStatus(ctx context.Context, clusterLifecycle *v1alpha2.ClusterLifecycle, opts v1.UpdateOptions) (*v1alpha2.ClusterLifecycle, error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateSubresourceAction(clusterlifecyclesResource, "status", clusterLifecycle), &v1alpha2.ClusterLifecycle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ClusterLifecycle), err +} + +// Delete takes name of the clusterLifecycle and deletes it. Returns an error if one occurs. +func (c *FakeClusterLifecycles) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteActionWithOptions(clusterlifecyclesResource, name, opts), &v1alpha2.ClusterLifecycle{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterLifecycles) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(clusterlifecyclesResource, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha2.ClusterLifecycleList{}) + return err +} + +// Patch applies the patch and returns the patched clusterLifecycle. +func (c *FakeClusterLifecycles) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ClusterLifecycle, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(clusterlifecyclesResource, name, pt, data, subresources...), &v1alpha2.ClusterLifecycle{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ClusterLifecycle), err +} diff --git a/pkg/client/clientset/versioned/typed/build/v1alpha2/generated_expansion.go b/pkg/client/clientset/versioned/typed/build/v1alpha2/generated_expansion.go index 198507aa7..d2346ffda 100644 --- a/pkg/client/clientset/versioned/typed/build/v1alpha2/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/build/v1alpha2/generated_expansion.go @@ -28,6 +28,8 @@ type ClusterBuilderExpansion interface{} type ClusterBuildpackExpansion interface{} +type ClusterLifecycleExpansion interface{} + type ClusterStackExpansion interface{} type ClusterStoreExpansion interface{} diff --git a/pkg/client/informers/externalversions/build/v1alpha2/clusterlifecycle.go b/pkg/client/informers/externalversions/build/v1alpha2/clusterlifecycle.go new file mode 100644 index 000000000..0a454cdf7 --- /dev/null +++ b/pkg/client/informers/externalversions/build/v1alpha2/clusterlifecycle.go @@ -0,0 +1,89 @@ +/* + * Copyright 2019 The original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + time "time" + + buildv1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + versioned "github.com/pivotal/kpack/pkg/client/clientset/versioned" + internalinterfaces "github.com/pivotal/kpack/pkg/client/informers/externalversions/internalinterfaces" + v1alpha2 "github.com/pivotal/kpack/pkg/client/listers/build/v1alpha2" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// ClusterLifecycleInformer provides access to a shared informer and lister for +// ClusterLifecycles. +type ClusterLifecycleInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.ClusterLifecycleLister +} + +type clusterLifecycleInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterLifecycleInformer constructs a new informer for ClusterLifecycle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewClusterLifecycleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterLifecycleInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterLifecycleInformer constructs a new informer for ClusterLifecycle type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredClusterLifecycleInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KpackV1alpha2().ClusterLifecycles().List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.KpackV1alpha2().ClusterLifecycles().Watch(context.TODO(), options) + }, + }, + &buildv1alpha2.ClusterLifecycle{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterLifecycleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterLifecycleInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterLifecycleInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&buildv1alpha2.ClusterLifecycle{}, f.defaultInformer) +} + +func (f *clusterLifecycleInformer) Lister() v1alpha2.ClusterLifecycleLister { + return v1alpha2.NewClusterLifecycleLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/build/v1alpha2/interface.go b/pkg/client/informers/externalversions/build/v1alpha2/interface.go index 97f4916c5..4dfda503a 100644 --- a/pkg/client/informers/externalversions/build/v1alpha2/interface.go +++ b/pkg/client/informers/externalversions/build/v1alpha2/interface.go @@ -34,6 +34,8 @@ type Interface interface { ClusterBuilders() ClusterBuilderInformer // ClusterBuildpacks returns a ClusterBuildpackInformer. ClusterBuildpacks() ClusterBuildpackInformer + // ClusterLifecycles returns a ClusterLifecycleInformer. + ClusterLifecycles() ClusterLifecycleInformer // ClusterStacks returns a ClusterStackInformer. ClusterStacks() ClusterStackInformer // ClusterStores returns a ClusterStoreInformer. @@ -80,6 +82,11 @@ func (v *version) ClusterBuildpacks() ClusterBuildpackInformer { return &clusterBuildpackInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } +// ClusterLifecycles returns a ClusterLifecycleInformer. +func (v *version) ClusterLifecycles() ClusterLifecycleInformer { + return &clusterLifecycleInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + // ClusterStacks returns a ClusterStackInformer. func (v *version) ClusterStacks() ClusterStackInformer { return &clusterStackInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 00a5f8a24..12a93c471 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -80,6 +80,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Kpack().V1alpha2().ClusterBuilders().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("clusterbuildpacks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Kpack().V1alpha2().ClusterBuildpacks().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("clusterlifecycles"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Kpack().V1alpha2().ClusterLifecycles().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("clusterstacks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Kpack().V1alpha2().ClusterStacks().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("clusterstores"): diff --git a/pkg/client/listers/build/v1alpha2/clusterlifecycle.go b/pkg/client/listers/build/v1alpha2/clusterlifecycle.go new file mode 100644 index 000000000..ea380641e --- /dev/null +++ b/pkg/client/listers/build/v1alpha2/clusterlifecycle.go @@ -0,0 +1,68 @@ +/* + * Copyright 2019 The original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + v1alpha2 "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterLifecycleLister helps list ClusterLifecycles. +// All objects returned here must be treated as read-only. +type ClusterLifecycleLister interface { + // List lists all ClusterLifecycles in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha2.ClusterLifecycle, err error) + // Get retrieves the ClusterLifecycle from the index for a given name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha2.ClusterLifecycle, error) + ClusterLifecycleListerExpansion +} + +// clusterLifecycleLister implements the ClusterLifecycleLister interface. +type clusterLifecycleLister struct { + indexer cache.Indexer +} + +// NewClusterLifecycleLister returns a new ClusterLifecycleLister. +func NewClusterLifecycleLister(indexer cache.Indexer) ClusterLifecycleLister { + return &clusterLifecycleLister{indexer: indexer} +} + +// List lists all ClusterLifecycles in the indexer. +func (s *clusterLifecycleLister) List(selector labels.Selector) (ret []*v1alpha2.ClusterLifecycle, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.ClusterLifecycle)) + }) + return ret, err +} + +// Get retrieves the ClusterLifecycle from the index for a given name. +func (s *clusterLifecycleLister) Get(name string) (*v1alpha2.ClusterLifecycle, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("clusterlifecycle"), name) + } + return obj.(*v1alpha2.ClusterLifecycle), nil +} diff --git a/pkg/client/listers/build/v1alpha2/expansion_generated.go b/pkg/client/listers/build/v1alpha2/expansion_generated.go index e369f428b..75027dde3 100644 --- a/pkg/client/listers/build/v1alpha2/expansion_generated.go +++ b/pkg/client/listers/build/v1alpha2/expansion_generated.go @@ -50,6 +50,10 @@ type ClusterBuilderListerExpansion interface{} // ClusterBuildpackLister. type ClusterBuildpackListerExpansion interface{} +// ClusterLifecycleListerExpansion allows custom methods to be added to +// ClusterLifecycleLister. +type ClusterLifecycleListerExpansion interface{} + // ClusterStackListerExpansion allows custom methods to be added to // ClusterStackLister. type ClusterStackListerExpansion interface{} From 72b48e53499267f08dbc13afff10575fbfa1860e Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 7 May 2024 15:13:41 -0400 Subject: [PATCH 03/39] Run hack/openapi-codegen.sh Signed-off-by: Natalie Arellano --- api/openapi-spec/swagger.json | 132 ++++++++++++++++ pkg/openapi/openapi_generated.go | 254 +++++++++++++++++++++++++++++++ 2 files changed, 386 insertions(+) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 3ae8ccd54..5bbde89c9 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -5721,6 +5721,9 @@ "x-kubernetes-patch-merge-key": "type", "x-kubernetes-patch-strategy": "merge" }, + "latestAttestationImage": { + "type": "string" + }, "latestCacheImage": { "type": "string" }, @@ -6250,6 +6253,112 @@ } } }, + "kpack.build.v1alpha2.ClusterLifecycle": { + "type": "object", + "required": [ + "spec", + "status" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.ClusterLifecycleSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.ClusterLifecycleStatus" + } + } + }, + "kpack.build.v1alpha2.ClusterLifecycleList": { + "type": "object", + "required": [ + "metadata", + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.ClusterLifecycle" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + } + }, + "kpack.build.v1alpha2.ClusterLifecycleSpec": { + "type": "object", + "properties": { + "image": { + "type": "string" + }, + "serviceAccountRef": { + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + } + } + }, + "kpack.build.v1alpha2.ClusterLifecycleStatus": { + "type": "object", + "properties": { + "buildpackAPIs": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/kpack.core.v1alpha1.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "platformAPIs": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "version": { + "description": "Id string `json:\"id,omitempty\"`", + "type": "string" + } + } + }, "kpack.build.v1alpha2.ClusterStack": { "type": "object", "required": [ @@ -6864,6 +6973,29 @@ } } }, + "kpack.build.v1alpha2.ResolvedClusterLifecycle": { + "type": "object", + "properties": { + "buildpackAPIs": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "platformAPIs": { + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "version": { + "description": "Id string `json:\"id,omitempty\"`", + "type": "string" + } + } + }, "kpack.build.v1alpha2.ResolvedClusterStack": { "type": "object", "properties": { diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index 84c8d8f76..a14a05762 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -89,6 +89,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterBuildpackList": schema_pkg_apis_build_v1alpha2_ClusterBuildpackList(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterBuildpackSpec": schema_pkg_apis_build_v1alpha2_ClusterBuildpackSpec(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterBuildpackStatus": schema_pkg_apis_build_v1alpha2_ClusterBuildpackStatus(ref), + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycle": schema_pkg_apis_build_v1alpha2_ClusterLifecycle(ref), + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleList": schema_pkg_apis_build_v1alpha2_ClusterLifecycleList(ref), + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleSpec": schema_pkg_apis_build_v1alpha2_ClusterLifecycleSpec(ref), + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleStatus": schema_pkg_apis_build_v1alpha2_ClusterLifecycleStatus(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterStack": schema_pkg_apis_build_v1alpha2_ClusterStack(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterStackList": schema_pkg_apis_build_v1alpha2_ClusterStackList(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterStackSpec": schema_pkg_apis_build_v1alpha2_ClusterStackSpec(ref), @@ -113,6 +117,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LastBuild": schema_pkg_apis_build_v1alpha2_LastBuild(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.NamespacedBuilderSpec": schema_pkg_apis_build_v1alpha2_NamespacedBuilderSpec(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.RegistryCache": schema_pkg_apis_build_v1alpha2_RegistryCache(ref), + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle": schema_pkg_apis_build_v1alpha2_ResolvedClusterLifecycle(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterStack": schema_pkg_apis_build_v1alpha2_ResolvedClusterStack(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.SourceResolver": schema_pkg_apis_build_v1alpha2_SourceResolver(ref), "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.SourceResolverList": schema_pkg_apis_build_v1alpha2_SourceResolverList(ref), @@ -2396,6 +2401,12 @@ func schema_pkg_apis_build_v1alpha2_BuildStatus(ref common.ReferenceCallback) co Format: "", }, }, + "latestAttestationImage": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "podName": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -3359,6 +3370,202 @@ func schema_pkg_apis_build_v1alpha2_ClusterBuildpackStatus(ref common.ReferenceC } } +func schema_pkg_apis_build_v1alpha2_ClusterLifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleStatus"), + }, + }, + }, + Required: []string{"spec", "status"}, + }, + }, + Dependencies: []string{ + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleSpec", "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycleStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_build_v1alpha2_ClusterLifecycleList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycle"), + }, + }, + }, + }, + }, + }, + Required: []string{"metadata", "items"}, + }, + }, + Dependencies: []string{ + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ClusterLifecycle", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + +func schema_pkg_apis_build_v1alpha2_ClusterLifecycleSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "image": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "serviceAccountRef": { + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ObjectReference"}, + } +} + +func schema_pkg_apis_build_v1alpha2_ClusterLifecycleStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition"), + }, + }, + }, + }, + }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Id string `json:\"id,omitempty\"`", + Type: []string{"string"}, + Format: "", + }, + }, + "buildpackAPIs": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "platformAPIs": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition"}, + } +} + func schema_pkg_apis_build_v1alpha2_ClusterStack(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -4532,6 +4739,53 @@ func schema_pkg_apis_build_v1alpha2_RegistryCache(ref common.ReferenceCallback) } } +func schema_pkg_apis_build_v1alpha2_ResolvedClusterLifecycle(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Id string `json:\"id,omitempty\"`", + Type: []string{"string"}, + Format: "", + }, + }, + "buildpackAPIs": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "platformAPIs": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_build_v1alpha2_ResolvedClusterStack(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ From 4541b204c9780aba65da9cefc03c27686b1aca7f Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 7 May 2024 15:16:17 -0400 Subject: [PATCH 04/39] Add CustomResourceDefinition Signed-off-by: Natalie Arellano --- config/clusterlifecycle.yaml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 config/clusterlifecycle.yaml diff --git a/config/clusterlifecycle.yaml b/config/clusterlifecycle.yaml new file mode 100644 index 000000000..6edaaeabf --- /dev/null +++ b/config/clusterlifecycle.yaml @@ -0,0 +1,41 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: clusterlifecycles.kpack.io +spec: + group: kpack.io + versions: + - name: v1alpha1 + served: true + storage: false + schema: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].status" + - name: v1alpha2 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + x-kubernetes-preserve-unknown-fields: true + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].status" + names: + kind: ClusterLifecycle + listKind: ClusterLifecycleList + singular: clusterlifecycle + plural: clusterlifecycles + categories: + - kpack + scope: Cluster From 224d6778334bcb32215974e6cee034602810c91c Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:17:06 -0400 Subject: [PATCH 05/39] Update config/controllerrole.yaml Signed-off-by: Natalie Arellano --- config/controllerrole.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/controllerrole.yaml b/config/controllerrole.yaml index b1e0bd264..fce11e440 100644 --- a/config/controllerrole.yaml +++ b/config/controllerrole.yaml @@ -27,6 +27,8 @@ rules: - clusterbuilders/status - clusterbuildpacks - clusterbuildpacks/status + - clusterlifecycles + - clusterlifecycles/status - clusterstores - clusterstores/status - clusterstacks From a98af3445723f5186cdff5f9d4b4ba31f1586fe0 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:24:39 -0400 Subject: [PATCH 06/39] Update cmd/controller/main.go to provide lifecycle informer to builder/cluster builder reconcilers Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 6 ++++-- pkg/reconciler/builder/builder.go | 1 + pkg/reconciler/clusterbuilder/clusterbuilder.go | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 90a73f24c..d8958d2f6 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -133,6 +133,7 @@ func main() { buildpackInformer := informerFactory.Kpack().V1alpha2().Buildpacks() clusterBuilderInformer := informerFactory.Kpack().V1alpha2().ClusterBuilders() clusterBuildpackInformer := informerFactory.Kpack().V1alpha2().ClusterBuildpacks() + clusterLifecycleInformer := informerFactory.Kpack().V1alpha2().ClusterLifecycles() clusterStoreInformer := informerFactory.Kpack().V1alpha2().ClusterStores() clusterStackInformer := informerFactory.Kpack().V1alpha2().ClusterStacks() @@ -229,12 +230,13 @@ func main() { buildController := build.NewController(ctx, options, k8sClient, buildInformer, podInformer, metadataRetriever, buildpodGenerator, podProgressLogger, keychainFactory, &slsaAttester, secretFetcher, featureFlags) imageController := image.NewController(ctx, options, k8sClient, imageInformer, buildInformer, duckBuilderInformer, sourceResolverInformer, pvcInformer, cfg.EnablePriorityClasses) sourceResolverController := sourceresolver.NewController(ctx, options, sourceResolverInformer, gitResolver, blobResolver, registryResolver) - builderController, builderResync := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, secretFetcher) + builderController, builderResync := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) buildpackController := buildpack.NewController(ctx, options, keychainFactory, buildpackInformer, remoteStoreReader) - clusterBuilderController, clusterBuilderResync := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, secretFetcher) + clusterBuilderController, clusterBuilderResync := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) clusterBuildpackController := clusterbuildpack.NewController(ctx, options, keychainFactory, clusterBuildpackInformer, remoteStoreReader) clusterStoreController := clusterstore.NewController(ctx, options, keychainFactory, clusterStoreInformer, remoteStoreReader) clusterStackController := clusterstack.NewController(ctx, options, keychainFactory, clusterStackInformer, remoteStackReader) + // TODO: should this be renamed to clusterLifecycleController? Should we create a new controller? lifecycleController := lifecycle.NewController(ctx, options, k8sClient, config.LifecycleConfigName, lifecycleConfigmapInformer, lifecycleProvider) lifecycleProvider.AddEventHandler(builderResync) diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 2592115f5..4993234fa 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -64,6 +64,7 @@ func NewController( buildpackInformer buildinformers.BuildpackInformer, clusterBuildpackInformer buildinformers.ClusterBuildpackInformer, clusterStackInformer buildinformers.ClusterStackInformer, + clusterLifecycleInformer buildinformers.ClusterLifecycleInformer, secretFetcher Fetcher, ) (*controller.Impl, func()) { c := &Reconciler{ diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index 20e670c9b..80a7703ec 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -62,6 +62,7 @@ func NewController( clusterStoreInformer buildinformers.ClusterStoreInformer, clusterBuildpackInformer buildinformers.ClusterBuildpackInformer, clusterStackInformer buildinformers.ClusterStackInformer, + clusterLifecycleInformer buildinformers.ClusterLifecycleInformer, secretFetcher Fetcher, ) (*controller.Impl, func()) { c := &Reconciler{ From adfd3ac2ba26479b0b46c840c500f2dcf656774d Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:35:49 -0400 Subject: [PATCH 07/39] Add lifecycle version to build status Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/build_types.go | 16 +++++++++------- .../build/v1alpha2/cluster_lifecycle_types.go | 1 + pkg/cnb/build_metadata.go | 1 + pkg/reconciler/build/build.go | 1 + 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pkg/apis/build/v1alpha2/build_types.go b/pkg/apis/build/v1alpha2/build_types.go index b7254e1b8..53867bae4 100644 --- a/pkg/apis/build/v1alpha2/build_types.go +++ b/pkg/apis/build/v1alpha2/build_types.go @@ -129,13 +129,15 @@ type BuildStack struct { // +k8s:openapi-gen=true type BuildStatus struct { - corev1alpha1.Status `json:",inline"` - BuildMetadata corev1alpha1.BuildpackMetadataList `json:"buildMetadata,omitempty"` - Stack corev1alpha1.BuildStack `json:"stack,omitempty"` - LatestImage string `json:"latestImage,omitempty"` - LatestCacheImage string `json:"latestCacheImage,omitempty"` - LatestAttestationImage string `json:"latestAttestationImage,omitempty"` - PodName string `json:"podName,omitempty"` + corev1alpha1.Status `json:",inline"` + BuildMetadata corev1alpha1.BuildpackMetadataList `json:"buildMetadata,omitempty"` + Stack corev1alpha1.BuildStack `json:"stack,omitempty"` + // TODO: confirm + Lifecycle ResolvedClusterLifecycle + LatestImage string `json:"latestImage,omitempty"` + LatestCacheImage string `json:"latestCacheImage,omitempty"` + LatestAttestationImage string `json:"latestAttestationImage,omitempty"` + PodName string `json:"podName,omitempty"` // +listType StepStates []corev1.ContainerState `json:"stepStates,omitempty"` // +listType diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index 7a4971e43..fd547423d 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -41,6 +41,7 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { + // TODO: should the unique ID be the image digest reference? // Id string `json:"id,omitempty"` Version string `json:"version,omitempty"` BuildpackAPIs []string `json:"buildpackAPIs,omitempty"` diff --git a/pkg/cnb/build_metadata.go b/pkg/cnb/build_metadata.go index 44cd4d2c3..65c297fa6 100644 --- a/pkg/cnb/build_metadata.go +++ b/pkg/cnb/build_metadata.go @@ -25,6 +25,7 @@ type BuildMetadata struct { LatestImage string `json:"latestImage"` StackID string `json:"stackID"` StackRunImage string `json:"stackRunImage"` + LifecycleVersion string `json:"lifecycleVersion"` } type ImageFetcher interface { diff --git a/pkg/reconciler/build/build.go b/pkg/reconciler/build/build.go index 033fa8dd7..9ee67e65f 100644 --- a/pkg/reconciler/build/build.go +++ b/pkg/reconciler/build/build.go @@ -215,6 +215,7 @@ func (c *Reconciler) reconcile(ctx context.Context, build *buildapi.Build) error build.Status.LatestAttestationImage = attestDigest build.Status.Stack.RunImage = buildMetadata.StackRunImage build.Status.Stack.ID = buildMetadata.StackID + build.Status.Lifecycle.Version = buildMetadata.LifecycleVersion } build.Status.PodName = pod.Name From 776756c57ad34f4279d4bab739e5516bb0164f18 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:41:41 -0400 Subject: [PATCH 08/39] Populate lifecycle version (for build status) in build metadata Signed-off-by: Natalie Arellano --- pkg/cnb/build_metadata.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/cnb/build_metadata.go b/pkg/cnb/build_metadata.go index 65c297fa6..71e42ab6c 100644 --- a/pkg/cnb/build_metadata.go +++ b/pkg/cnb/build_metadata.go @@ -25,7 +25,8 @@ type BuildMetadata struct { LatestImage string `json:"latestImage"` StackID string `json:"stackID"` StackRunImage string `json:"stackRunImage"` - LifecycleVersion string `json:"lifecycleVersion"` + // TODO: lifecycle git commit is also available + LifecycleVersion string `json:"lifecycleVersion"` } type ImageFetcher interface { @@ -49,6 +50,7 @@ func (r *RemoteMetadataRetriever) GetBuildMetadata(builtImageRef, cacheTag strin LatestCacheImage: cacheImageRef, StackRunImage: buildImg.stack.RunImage, StackID: buildImg.stack.ID, + LifecycleVersion: buildImg.lifecycle.version, }, nil } @@ -108,6 +110,7 @@ func readBuiltImage(appImage ggcrv1.Image, appImageId string) (builtImage, error RunImage: baseImageRef.Context().String() + "@" + runImageRef.Identifier(), ID: stackId, }, + lifecycle: builtImageLifecycle{version: buildMetadata.Launcher.Version}, }, nil } @@ -115,6 +118,11 @@ type builtImage struct { identifier string buildpackMetadata []lifecyclebuildpack.GroupElement stack builtImageStack + lifecycle builtImageLifecycle +} + +type builtImageLifecycle struct { + version string } type appLayersMetadata struct { From 622650fad5a1104c2236458e3bdc50db7f106611 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:44:03 -0400 Subject: [PATCH 09/39] Register ClusterLifecycle types Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/register.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/apis/build/v1alpha2/register.go b/pkg/apis/build/v1alpha2/register.go index 8c42a86e8..2124023a9 100644 --- a/pkg/apis/build/v1alpha2/register.go +++ b/pkg/apis/build/v1alpha2/register.go @@ -59,6 +59,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &SourceResolverList{}, &ClusterStack{}, &ClusterStackList{}, + &ClusterLifecycle{}, + &ClusterLifecycleList{}, &ClusterStore{}, &ClusterStoreList{}, &ClusterBuildpack{}, From 428fb07e1292ec77a35ac3240504e0e9e8ce4093 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:45:59 -0400 Subject: [PATCH 10/39] Add lifecycle as a build reason Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/image_builds.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/apis/build/v1alpha2/image_builds.go b/pkg/apis/build/v1alpha2/image_builds.go index 728f77faa..e48c7a5fd 100644 --- a/pkg/apis/build/v1alpha2/image_builds.go +++ b/pkg/apis/build/v1alpha2/image_builds.go @@ -29,6 +29,8 @@ const ( BuildReasonCommit = "COMMIT" BuildReasonBuildpack = "BUILDPACK" BuildReasonStack = "STACK" + // TODO: use this somewhere + BuildReasonLifecycle = "LIFECYCLE" BuildReasonTrigger = "TRIGGER" ) From 86c924f2fde3f1ce1f38ffd97de06c2ccde40b8e Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 11:55:16 -0400 Subject: [PATCH 11/39] Update cmd/webhook/main.go to add ClusterLifecycleKind to known generic CRDs Signed-off-by: Natalie Arellano --- cmd/webhook/main.go | 1 + .../v1alpha2/cluster_lifecycle_validation.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 140f3ea04..5ca12a3e5 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -39,6 +39,7 @@ var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ v1alpha2.SchemeGroupVersion.WithKind(v1alpha2.ClusterBuildpackKind): &v1alpha2.ClusterBuildpack{}, v1alpha2.SchemeGroupVersion.WithKind(v1alpha2.ClusterStoreKind): &v1alpha2.ClusterStore{}, v1alpha2.SchemeGroupVersion.WithKind(v1alpha2.ClusterStackKind): &v1alpha2.ClusterStack{}, + v1alpha2.SchemeGroupVersion.WithKind(v1alpha2.ClusterLifecycleKind): &v1alpha2.ClusterLifecycle{}, } func init() { diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go new file mode 100644 index 000000000..c73a6557a --- /dev/null +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go @@ -0,0 +1,17 @@ +package v1alpha2 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +func (s *ClusterLifecycle) SetDefaults(context.Context) { + // TODO +} + +func (s *ClusterLifecycle) Validate(ctx context.Context) *apis.FieldError { + //return s.Spec.Validate(ctx).ViaField("spec") + // TODO + return nil +} From 8e84c407c2575769ad34dc967f88c44457498e2d Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 12:00:57 -0400 Subject: [PATCH 12/39] Add lifecycle to builder spec, builder status, and builder record Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/builder_lifecycle.go | 2 ++ pkg/apis/build/v1alpha2/builder_types.go | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/apis/build/v1alpha2/builder_lifecycle.go b/pkg/apis/build/v1alpha2/builder_lifecycle.go index c2913fc1f..3f4dbfd2c 100644 --- a/pkg/apis/build/v1alpha2/builder_lifecycle.go +++ b/pkg/apis/build/v1alpha2/builder_lifecycle.go @@ -11,6 +11,7 @@ import ( type BuilderRecord struct { Image string Stack corev1alpha1.BuildStack + Lifecycle ResolvedClusterLifecycle Buildpacks corev1alpha1.BuildpackMetadataList Order []corev1alpha1.OrderEntry ObservedStoreGeneration int64 @@ -21,6 +22,7 @@ type BuilderRecord struct { func (bs *BuilderStatus) BuilderRecord(record BuilderRecord) { bs.Stack = record.Stack + bs.Lifecycle = record.Lifecycle bs.BuilderMetadata = record.Buildpacks bs.LatestImage = record.Image bs.Conditions = corev1alpha1.Conditions{ diff --git a/pkg/apis/build/v1alpha2/builder_types.go b/pkg/apis/build/v1alpha2/builder_types.go index 48bccde5c..b236a5de8 100644 --- a/pkg/apis/build/v1alpha2/builder_types.go +++ b/pkg/apis/build/v1alpha2/builder_types.go @@ -34,7 +34,9 @@ type Builder struct { type BuilderSpec struct { Tag string `json:"tag,omitempty"` Stack corev1.ObjectReference `json:"stack,omitempty"` - Store corev1.ObjectReference `json:"store,omitempty"` + // TODO: should `json:"lifecycle,...` actually be `json:"clusterlifecycle,...`? + Lifecycle corev1.ObjectReference `json:"lifecycle,omitempty"` + Store corev1.ObjectReference `json:"store,omitempty"` // +listType Order []BuilderOrderEntry `json:"order,omitempty"` AdditionalLabels map[string]string `json:"additionalLabels,omitempty"` @@ -72,6 +74,7 @@ type BuilderStatus struct { BuilderMetadata corev1alpha1.BuildpackMetadataList `json:"builderMetadata,omitempty"` Order []corev1alpha1.OrderEntry `json:"order,omitempty"` Stack corev1alpha1.BuildStack `json:"stack,omitempty"` + Lifecycle ResolvedClusterLifecycle `json:"lifecycle,omitempty"` LatestImage string `json:"latestImage,omitempty"` ObservedStackGeneration int64 `json:"observedStackGeneration,omitempty"` ObservedStoreGeneration int64 `json:"observedStoreGeneration,omitempty"` From add47fa2ed6c0151aaee4d53ce3e690ba1f33332 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 8 May 2024 12:33:36 -0400 Subject: [PATCH 13/39] Add TODO Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/builder_resource.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/apis/build/v1alpha2/builder_resource.go b/pkg/apis/build/v1alpha2/builder_resource.go index f34c64abb..2fe0d2ca2 100644 --- a/pkg/apis/build/v1alpha2/builder_resource.go +++ b/pkg/apis/build/v1alpha2/builder_resource.go @@ -10,6 +10,7 @@ type BuilderResource interface { UpToDate() bool BuildpackMetadata() corev1alpha1.BuildpackMetadataList RunImage() string + // TODO: add LifecycleVersion here? GetKind() string ConditionReadyMessage() string } From 0cf19a85a1f92d3d67eadc8703f419ab5ef07eb1 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 21 May 2024 16:29:15 -0400 Subject: [PATCH 14/39] clusterlifecycle tests pass, I guess Signed-off-by: Natalie Arellano --- .../clusterlifecycle/clusterlifecycle.go | 145 +++++++++++ .../clusterlifecycle/clusterlifecycle_test.go | 227 ++++++++++++++++++ .../fake_cluster_lifecycle_reader.go | 120 +++++++++ pkg/reconciler/testhelpers/listers.go | 4 + 4 files changed, 496 insertions(+) create mode 100644 pkg/reconciler/clusterlifecycle/clusterlifecycle.go create mode 100644 pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go create mode 100644 pkg/reconciler/clusterlifecycle/clusterlifecyclefakes/fake_cluster_lifecycle_reader.go diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecycle.go b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go new file mode 100644 index 000000000..50f876a76 --- /dev/null +++ b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go @@ -0,0 +1,145 @@ +package clusterlifecycle + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/authn" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/equality" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/cache" + "knative.dev/pkg/controller" + "knative.dev/pkg/logging/logkey" + + buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" + "github.com/pivotal/kpack/pkg/client/clientset/versioned" + buildinformers "github.com/pivotal/kpack/pkg/client/informers/externalversions/build/v1alpha2" + buildlisters "github.com/pivotal/kpack/pkg/client/listers/build/v1alpha2" + "github.com/pivotal/kpack/pkg/reconciler" + "github.com/pivotal/kpack/pkg/registry" +) + +const ( + ReconcilerName = "Lifecycles" + Kind = "Lifecycle" +) + +//go:generate counterfeiter . ClusterLifecycleReader +type ClusterLifecycleReader interface { + Read(keychain authn.Keychain, clusterLifecycleSpec buildapi.ClusterLifecycleSpec) (buildapi.ResolvedClusterLifecycle, error) +} + +func NewController( + ctx context.Context, + opt reconciler.Options, + keychainFactory registry.KeychainFactory, + clusterLifecycleInformer buildinformers.ClusterLifecycleInformer, + clusterLifecycleReader ClusterLifecycleReader, +) *controller.Impl { + c := &Reconciler{ + Client: opt.Client, + ClusterLifecycleLister: clusterLifecycleInformer.Lister(), + ClusterLifecycleReader: clusterLifecycleReader, + KeychainFactory: keychainFactory, + } + + logger := opt.Logger.With( + zap.String(logkey.Kind, buildapi.ClusterLifecycleCRName), + ) + + impl := controller.NewContext( + ctx, + &reconciler.NetworkErrorReconciler{ + Reconciler: c, + }, + controller.ControllerOptions{WorkQueueName: ReconcilerName, Logger: logger}, + ) + clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + return impl +} + +type Reconciler struct { + Client versioned.Interface + ClusterLifecycleLister buildlisters.ClusterLifecycleLister + ClusterLifecycleReader ClusterLifecycleReader + KeychainFactory registry.KeychainFactory +} + +func (c *Reconciler) Reconcile(ctx context.Context, key string) error { + _, clusterLifecycleName, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return err + } + + clusterLifecycle, err := c.ClusterLifecycleLister.Get(clusterLifecycleName) + if k8serrors.IsNotFound(err) { + return nil + } else if err != nil { + return err + } + + clusterLifecycle = clusterLifecycle.DeepCopy() + + clusterLifecycle, err = c.reconcileClusterLifecycleStatus(ctx, clusterLifecycle) + + updateErr := c.updateClusterLifecycleStatus(ctx, clusterLifecycle) + if updateErr != nil { + return updateErr + } + + if err != nil { + return err + } + return nil +} + +func (c *Reconciler) reconcileClusterLifecycleStatus(ctx context.Context, clusterLifecycle *buildapi.ClusterLifecycle) (*buildapi.ClusterLifecycle, error) { + secretRef := registry.SecretRef{} + + if clusterLifecycle.Spec.ServiceAccountRef != nil { + secretRef = registry.SecretRef{ + ServiceAccount: clusterLifecycle.Spec.ServiceAccountRef.Name, + Namespace: clusterLifecycle.Spec.ServiceAccountRef.Namespace, + } + } + + keychain, err := c.KeychainFactory.KeychainForSecretRef(ctx, secretRef) + if err != nil { + clusterLifecycle.Status = buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.CreateStatusWithReadyCondition(clusterLifecycle.Generation, err), + } + return clusterLifecycle, err + } + + resolvedClusterLifecycle, err := c.ClusterLifecycleReader.Read(keychain, clusterLifecycle.Spec) + if err != nil { + clusterLifecycle.Status = buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.CreateStatusWithReadyCondition(clusterLifecycle.Generation, err), + } + return clusterLifecycle, err + } + + clusterLifecycle.Status = buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.CreateStatusWithReadyCondition(clusterLifecycle.Generation, nil), + ResolvedClusterLifecycle: resolvedClusterLifecycle, + } + return clusterLifecycle, nil +} + +func (c *Reconciler) updateClusterLifecycleStatus(ctx context.Context, desired *buildapi.ClusterLifecycle) error { + desired.Status.ObservedGeneration = desired.Generation + + original, err := c.ClusterLifecycleLister.Get(desired.Name) + if err != nil { + return err + } + + if equality.Semantic.DeepEqual(desired.Status, original.Status) { + return nil + } + + _, err = c.Client.KpackV1alpha2().ClusterLifecycles().UpdateStatus(ctx, desired, metav1.UpdateOptions{}) + return err +} diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go b/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go new file mode 100644 index 000000000..88a2a5d0e --- /dev/null +++ b/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go @@ -0,0 +1,227 @@ +package clusterlifecycle_test + +import ( + "errors" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + clientgotesting "k8s.io/client-go/testing" + "k8s.io/client-go/tools/record" + "knative.dev/pkg/controller" + rtesting "knative.dev/pkg/reconciler/testing" + + buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" + "github.com/pivotal/kpack/pkg/client/clientset/versioned/fake" + kreconciler "github.com/pivotal/kpack/pkg/reconciler" + "github.com/pivotal/kpack/pkg/reconciler/clusterlifecycle" + "github.com/pivotal/kpack/pkg/reconciler/clusterlifecycle/clusterlifecyclefakes" + "github.com/pivotal/kpack/pkg/reconciler/testhelpers" + "github.com/pivotal/kpack/pkg/registry" + "github.com/pivotal/kpack/pkg/registry/registryfakes" +) + +func TestClusterLifecycleReconciler(t *testing.T) { + spec.Run(t, "Lifecycle Reconciler", testClusterLifecycleReconciler) +} + +func testClusterLifecycleReconciler(t *testing.T, when spec.G, it spec.S) { + const ( + clusterLifecycleName = "some-clusterLifecycle" + clusterLifecycleKey = clusterLifecycleName + initialGeneration int64 = 1 + ) + + var ( + fakeKeyChainFactory = ®istryfakes.FakeKeychainFactory{} + ) + + fakeClusterLifecycleReader := &clusterlifecyclefakes.FakeClusterLifecycleReader{} + + testClusterLifecycle := &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterLifecycleName, + Generation: initialGeneration, + }, + Spec: buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{ + Image: "some-registry.io/lifecycle-image", + }, + }, + } + + rt := testhelpers.ReconcilerTester(t, + func(t *testing.T, row *rtesting.TableRow) (reconciler controller.Reconciler, lists rtesting.ActionRecorderList, list rtesting.EventList) { + listers := testhelpers.NewListers(row.Objects) + fakeClient := fake.NewSimpleClientset(listers.BuildServiceObjects()...) + r := &clusterlifecycle.Reconciler{ + Client: fakeClient, + ClusterLifecycleLister: listers.GetClusterLifecycleLister(), + ClusterLifecycleReader: fakeClusterLifecycleReader, + KeychainFactory: fakeKeyChainFactory, + } + return &kreconciler.NetworkErrorReconciler{Reconciler: r}, rtesting.ActionRecorderList{fakeClient}, rtesting.EventList{Recorder: record.NewFakeRecorder(10)} + }) + + when("#Reconcile", func() { + it("saves metadata to the status", func() { + resolvedClusterLifecycle := buildapi.ResolvedClusterLifecycle{ + Version: "some-version", + BuildpackAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11"}, + PlatformAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, + } + fakeClusterLifecycleReader.ReadReturns(resolvedClusterLifecycle, nil) + emptySecretRef := registry.SecretRef{} + defaultKeyChain := ®istryfakes.FakeKeychain{Name: "default"} + fakeKeyChainFactory.AddKeychainForSecretRef(t, emptySecretRef, defaultKeyChain) + + rt.Test(rtesting.TableRow{ + Key: clusterLifecycleKey, + Objects: []runtime.Object{ + testClusterLifecycle, + }, + WantErr: false, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: &buildapi.ClusterLifecycle{ + ObjectMeta: testClusterLifecycle.ObjectMeta, + Spec: testClusterLifecycle.Spec, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 1, + Conditions: corev1alpha1.Conditions{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + ResolvedClusterLifecycle: resolvedClusterLifecycle, + }, + }, + }, + }, + }) + + require.Equal(t, 1, fakeClusterLifecycleReader.ReadCallCount()) + _, clusterLifecycleSpec := fakeClusterLifecycleReader.ReadArgsForCall(0) + require.Equal(t, testClusterLifecycle.Spec, clusterLifecycleSpec) + }) + + it("does not update the status with no status change", func() { + resolvedClusterLifecycle := buildapi.ResolvedClusterLifecycle{ + Version: "some-version", + BuildpackAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11"}, + PlatformAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, + } + fakeClusterLifecycleReader.ReadReturns(resolvedClusterLifecycle, nil) + emptySecretRef := registry.SecretRef{} + defaultKeyChain := ®istryfakes.FakeKeychain{Name: "default"} + fakeKeyChainFactory.AddKeychainForSecretRef(t, emptySecretRef, defaultKeyChain) + + testClusterLifecycle.Status = buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 1, + Conditions: corev1alpha1.Conditions{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + ResolvedClusterLifecycle: resolvedClusterLifecycle, + } + rt.Test(rtesting.TableRow{ + Key: clusterLifecycleKey, + Objects: []runtime.Object{ + testClusterLifecycle, + }, + WantErr: false, + }) + }) + + it("sets the status to Ready False if error reading from clusterLifecycle", func() { + fakeClusterLifecycleReader.ReadReturns(buildapi.ResolvedClusterLifecycle{}, errors.New("invalid mixins on run image")) + emptySecretRef := registry.SecretRef{} + defaultKeyChain := ®istryfakes.FakeKeychain{Name: "default"} + fakeKeyChainFactory.AddKeychainForSecretRef(t, emptySecretRef, defaultKeyChain) + + rt.Test(rtesting.TableRow{ + Key: clusterLifecycleKey, + Objects: []runtime.Object{ + testClusterLifecycle, + }, + WantErr: true, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: &buildapi.ClusterLifecycle{ + ObjectMeta: testClusterLifecycle.ObjectMeta, + Spec: testClusterLifecycle.Spec, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 1, + Conditions: corev1alpha1.Conditions{ + { + Message: "invalid mixins on run image", + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionFalse, + }, + }, + }, + }, + }, + }, + }, + }) + }) + + it("uses the keychain of the referenced service account", func() { + fakeClusterLifecycleReader.ReadReturns(buildapi.ResolvedClusterLifecycle{}, nil) + + testClusterLifecycle.Spec.ServiceAccountRef = &corev1.ObjectReference{Name: "private-account", Namespace: "my-namespace"} + secretRef := registry.SecretRef{ + ServiceAccount: "private-account", + Namespace: "my-namespace", + } + expectedKeyChain := ®istryfakes.FakeKeychain{Name: "secret"} + fakeKeyChainFactory.AddKeychainForSecretRef(t, secretRef, expectedKeyChain) + + rt.Test(rtesting.TableRow{ + Key: clusterLifecycleKey, + Objects: []runtime.Object{ + testClusterLifecycle, + }, + WantErr: false, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: &buildapi.ClusterLifecycle{ + ObjectMeta: testClusterLifecycle.ObjectMeta, + Spec: testClusterLifecycle.Spec, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 1, + Conditions: corev1alpha1.Conditions{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + }, + }, + }, + }) + + assert.Equal(t, 1, fakeClusterLifecycleReader.ReadCallCount()) + actualKeyChain, _ := fakeClusterLifecycleReader.ReadArgsForCall(0) + assert.Equal(t, expectedKeyChain, actualKeyChain) + }) + + }) +} diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecyclefakes/fake_cluster_lifecycle_reader.go b/pkg/reconciler/clusterlifecycle/clusterlifecyclefakes/fake_cluster_lifecycle_reader.go new file mode 100644 index 000000000..3a2284cdb --- /dev/null +++ b/pkg/reconciler/clusterlifecycle/clusterlifecyclefakes/fake_cluster_lifecycle_reader.go @@ -0,0 +1,120 @@ +// Code generated by counterfeiter. DO NOT EDIT. +package clusterlifecyclefakes + +import ( + "sync" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + "github.com/pivotal/kpack/pkg/reconciler/clusterlifecycle" +) + +type FakeClusterLifecycleReader struct { + ReadStub func(authn.Keychain, v1alpha2.ClusterLifecycleSpec) (v1alpha2.ResolvedClusterLifecycle, error) + readMutex sync.RWMutex + readArgsForCall []struct { + arg1 authn.Keychain + arg2 v1alpha2.ClusterLifecycleSpec + } + readReturns struct { + result1 v1alpha2.ResolvedClusterLifecycle + result2 error + } + readReturnsOnCall map[int]struct { + result1 v1alpha2.ResolvedClusterLifecycle + result2 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeClusterLifecycleReader) Read(arg1 authn.Keychain, arg2 v1alpha2.ClusterLifecycleSpec) (v1alpha2.ResolvedClusterLifecycle, error) { + fake.readMutex.Lock() + ret, specificReturn := fake.readReturnsOnCall[len(fake.readArgsForCall)] + fake.readArgsForCall = append(fake.readArgsForCall, struct { + arg1 authn.Keychain + arg2 v1alpha2.ClusterLifecycleSpec + }{arg1, arg2}) + stub := fake.ReadStub + fakeReturns := fake.readReturns + fake.recordInvocation("Read", []interface{}{arg1, arg2}) + fake.readMutex.Unlock() + if stub != nil { + return stub(arg1, arg2) + } + if specificReturn { + return ret.result1, ret.result2 + } + return fakeReturns.result1, fakeReturns.result2 +} + +func (fake *FakeClusterLifecycleReader) ReadCallCount() int { + fake.readMutex.RLock() + defer fake.readMutex.RUnlock() + return len(fake.readArgsForCall) +} + +func (fake *FakeClusterLifecycleReader) ReadCalls(stub func(authn.Keychain, v1alpha2.ClusterLifecycleSpec) (v1alpha2.ResolvedClusterLifecycle, error)) { + fake.readMutex.Lock() + defer fake.readMutex.Unlock() + fake.ReadStub = stub +} + +func (fake *FakeClusterLifecycleReader) ReadArgsForCall(i int) (authn.Keychain, v1alpha2.ClusterLifecycleSpec) { + fake.readMutex.RLock() + defer fake.readMutex.RUnlock() + argsForCall := fake.readArgsForCall[i] + return argsForCall.arg1, argsForCall.arg2 +} + +func (fake *FakeClusterLifecycleReader) ReadReturns(result1 v1alpha2.ResolvedClusterLifecycle, result2 error) { + fake.readMutex.Lock() + defer fake.readMutex.Unlock() + fake.ReadStub = nil + fake.readReturns = struct { + result1 v1alpha2.ResolvedClusterLifecycle + result2 error + }{result1, result2} +} + +func (fake *FakeClusterLifecycleReader) ReadReturnsOnCall(i int, result1 v1alpha2.ResolvedClusterLifecycle, result2 error) { + fake.readMutex.Lock() + defer fake.readMutex.Unlock() + fake.ReadStub = nil + if fake.readReturnsOnCall == nil { + fake.readReturnsOnCall = make(map[int]struct { + result1 v1alpha2.ResolvedClusterLifecycle + result2 error + }) + } + fake.readReturnsOnCall[i] = struct { + result1 v1alpha2.ResolvedClusterLifecycle + result2 error + }{result1, result2} +} + +func (fake *FakeClusterLifecycleReader) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.readMutex.RLock() + defer fake.readMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakeClusterLifecycleReader) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + +var _ clusterlifecycle.ClusterLifecycleReader = new(FakeClusterLifecycleReader) diff --git a/pkg/reconciler/testhelpers/listers.go b/pkg/reconciler/testhelpers/listers.go index 858b33ddd..3be82de43 100644 --- a/pkg/reconciler/testhelpers/listers.go +++ b/pkg/reconciler/testhelpers/listers.go @@ -83,6 +83,10 @@ func (l *Listers) GetClusterStackLister() buildlisters.ClusterStackLister { return buildlisters.NewClusterStackLister(l.indexerFor(&buildapi.ClusterStack{})) } +func (l *Listers) GetClusterLifecycleLister() buildlisters.ClusterLifecycleLister { + return buildlisters.NewClusterLifecycleLister(l.indexerFor(&buildapi.ClusterLifecycle{})) +} + func (l *Listers) GetSourceResolverLister() buildlisters.SourceResolverLister { return buildlisters.NewSourceResolverLister(l.indexerFor(&buildapi.SourceResolver{})) } From fda81bf9021be0525eb3c0fb0d24ecd89c83e220 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 11:39:41 -0400 Subject: [PATCH 15/39] Passing units for pkg/reconciler/builder/builder_test.go Signed-off-by: Natalie Arellano --- pkg/cnb/create_builder.go | 2 +- pkg/reconciler/builder/builder.go | 20 +++++++++ pkg/reconciler/builder/builder_test.go | 45 ++++++++++++++++++- .../clusterbuilder/clusterbuilder.go | 2 + .../testhelpers/fake_builder_creator.go | 14 +++++- 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 09bb2eee0..911d7324a 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -37,11 +37,11 @@ func (r *RemoteBuilderCreator) CreateBuilder( stackKeychain authn.Keychain, fetcher RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, + clusterLifecycle *buildapi.ClusterLifecycle, spec buildapi.BuilderSpec, serviceAccountSecrets []*corev1.Secret, resolvedBuilderRef string, ) (buildapi.BuilderRecord, error) { - buildImage, _, err := r.RegistryClient.Fetch(stackKeychain, clusterStack.Status.BuildImage.LatestImage) if err != nil { return buildapi.BuilderRecord{}, err diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 4993234fa..889bd489b 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -44,6 +44,7 @@ type BuilderCreator interface { keychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, + clusterLifecycle *buildapi.ClusterLifecycle, spec buildapi.BuilderSpec, serviceAccountSecrets []*corev1.Secret, resolvedBuilderRef string, @@ -76,6 +77,7 @@ func NewController( BuildpackLister: buildpackInformer.Lister(), ClusterBuildpackLister: clusterBuildpackInformer.Lister(), ClusterStackLister: clusterStackInformer.Lister(), + ClusterLifecycleLister: clusterLifecycleInformer.Lister(), SecretFetcher: secretFetcher, } @@ -129,6 +131,7 @@ type Reconciler struct { BuildpackLister buildlisters.BuildpackLister ClusterBuildpackLister buildlisters.ClusterBuildpackLister ClusterStackLister buildlisters.ClusterStackLister + ClusterLifecycleLister buildlisters.ClusterLifecycleLister SecretFetcher Fetcher } @@ -174,6 +177,17 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui }, }, builder.NamespacedName()) + c.Tracker.Track(reconciler.Key{ + NamespacedName: types.NamespacedName{ + Name: builder.Spec.Lifecycle.Name, // TODO: check + Namespace: metav1.NamespaceAll, + }, + GroupKind: schema.GroupKind{ + Group: "kpack.io", + Kind: buildapi.ClusterLifecycleKind, + }, + }, builder.NamespacedName()) + var ( clusterStore *buildapi.ClusterStore err error @@ -225,6 +239,11 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui return buildapi.BuilderRecord{}, errors.Errorf("Error: clusterstack '%s' is not ready", clusterStack.Name) } + clusterLifecycle, err := c.ClusterLifecycleLister.Get(builder.Spec.Lifecycle.Name) // TODO: check + if err != nil { + return buildapi.BuilderRecord{}, err + } + builderKeychain, err := c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ ServiceAccount: builder.Spec.ServiceAccount(), Namespace: builder.Namespace, @@ -262,6 +281,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui stackKeychain, fetcher, clusterStack, + clusterLifecycle, builder.Spec.BuilderSpec, serviceAccountSecrets, resolvedBuilderRef, diff --git a/pkg/reconciler/builder/builder_test.go b/pkg/reconciler/builder/builder_test.go index 74f0ec7f5..48002678c 100644 --- a/pkg/reconciler/builder/builder_test.go +++ b/pkg/reconciler/builder/builder_test.go @@ -70,6 +70,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { BuildpackLister: listers.GetBuildpackLister(), ClusterBuildpackLister: listers.GetClusterBuildpackLister(), ClusterStackLister: listers.GetClusterStackLister(), + ClusterLifecycleLister: listers.GetClusterLifecycleLister(), SecretFetcher: fakeSecretFetcher, } return &kreconciler.NetworkErrorReconciler{Reconciler: r}, rtesting.ActionRecorderList{fakeClient, k8sfakeClient}, rtesting.EventList{Recorder: record.NewFakeRecorder(10)} @@ -133,6 +134,33 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { }, } + clusterLifecycle := &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "some-lifecycle", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterLifecycle", + APIVersion: "kpack.io/v1alpha2", + }, + Spec: buildapi.ClusterLifecycleSpec{ + ServiceAccountRef: &corev1.ObjectReference{ + Name: "some-service-account", + Namespace: testNamespace, + }, + }, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 0, + Conditions: []corev1alpha1.Condition{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + } + buildpack := &buildapi.Buildpack{ ObjectMeta: metav1.ObjectMeta{ Name: "buildpack.id.3", @@ -167,6 +195,10 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { Kind: "Stack", Name: "some-stack", }, + Lifecycle: corev1.ObjectReference{ + Kind: "Lifecycle", + Name: "some-lifecycle", + }, Store: corev1.ObjectReference{ Kind: "ClusterStore", Name: "some-store", @@ -277,6 +309,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -299,6 +332,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { StackKeychain: ®istryfakes.FakeKeychain{}, Fetcher: expectedFetcher, ClusterStack: clusterStack, + ClusterLifecycle: clusterLifecycle, BuilderSpec: builder.Spec.BuilderSpec, SigningSecrets: []*corev1.Secret{}, ResolvedBuilderTag: expectedResolvedTag, @@ -333,7 +367,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { }, }, BuilderMetadata: []corev1alpha1.BuildpackMetadata{}, - Stack: corev1alpha1.BuildStack{ + Stack: corev1alpha1.BuildStack{ // TODO: should we check lifecycle on the expected builder? RunImage: "example.com/run-image@sha256:123456", ID: "fake.stack.id", }, @@ -344,6 +378,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, buildpack, @@ -361,6 +396,9 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { require.True(t, fakeTracker.IsTracking( kreconciler.KeyForObject(clusterStack), builder.NamespacedName())) + require.True(t, fakeTracker.IsTracking( + kreconciler.KeyForObject(clusterLifecycle), + builder.NamespacedName())) require.True(t, fakeTracker.IsTrackingKind( kreconciler.KeyForObject(buildpack).GroupKind, @@ -415,6 +453,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -431,6 +470,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -622,6 +662,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -664,6 +705,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -705,6 +747,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index 80a7703ec..84c2a4d56 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -43,6 +43,7 @@ type BuilderCreator interface { stackKeychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, + clusterLifecycle *buildapi.ClusterLifecycle, spec buildapi.BuilderSpec, serviceAccountSecrets []*corev1.Secret, resolvedBuilderRef string, @@ -245,6 +246,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu stackKeychain, fetcher, clusterStack, + nil, // TODO: fix builder.Spec.BuilderSpec, serviceAccountSecrets, resolvedBuilderRef, diff --git a/pkg/reconciler/testhelpers/fake_builder_creator.go b/pkg/reconciler/testhelpers/fake_builder_creator.go index d4f5f6567..6d6490f4c 100644 --- a/pkg/reconciler/testhelpers/fake_builder_creator.go +++ b/pkg/reconciler/testhelpers/fake_builder_creator.go @@ -24,18 +24,30 @@ type CreateBuilderArgs struct { StackKeychain authn.Keychain Fetcher cnb.RemoteBuildpackFetcher ClusterStack *buildapi.ClusterStack + ClusterLifecycle *buildapi.ClusterLifecycle BuilderSpec buildapi.BuilderSpec SigningSecrets []*corev1.Secret ResolvedBuilderTag string } -func (f *FakeBuilderCreator) CreateBuilder(ctx context.Context, builderKeychain authn.Keychain, stackKeychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, spec buildapi.BuilderSpec, signingSecrets []*corev1.Secret, resolvedBuilderTag string) (buildapi.BuilderRecord, error) { +func (f *FakeBuilderCreator) CreateBuilder( + ctx context.Context, + builderKeychain authn.Keychain, + stackKeychain authn.Keychain, + fetcher cnb.RemoteBuildpackFetcher, + clusterStack *buildapi.ClusterStack, + clusterLifecycle *buildapi.ClusterLifecycle, + spec buildapi.BuilderSpec, + signingSecrets []*corev1.Secret, + resolvedBuilderTag string, +) (buildapi.BuilderRecord, error) { f.CreateBuilderCalls = append(f.CreateBuilderCalls, CreateBuilderArgs{ Context: ctx, BuilderKeychain: builderKeychain, StackKeychain: stackKeychain, Fetcher: fetcher, ClusterStack: clusterStack, + ClusterLifecycle: clusterLifecycle, BuilderSpec: spec, SigningSecrets: signingSecrets, ResolvedBuilderTag: resolvedBuilderTag, From 9d0db3ac9f74dc34535fcda6e1e559944bcb1205 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 11:40:22 -0400 Subject: [PATCH 16/39] Add TODO Signed-off-by: Natalie Arellano --- pkg/reconciler/builder/builder.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 889bd489b..87afaa2e5 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -105,6 +105,12 @@ func NewController( c.Tracker.OnChanged, buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterStackKind)), )) + // TODO: how is this tested? + //clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( + // controller.EnsureTypeMeta( + // c.Tracker.OnChanged, + // buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), + //)) buildpackInformer.Informer().AddEventHandler(controller.HandleAll( controller.EnsureTypeMeta( c.Tracker.OnChanged, From d3cc9d345ebb4b4eb7e046369a3fb5bbd8852645 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 12:01:44 -0400 Subject: [PATCH 17/39] Passing units for pkg/reconciler/clusterbuilder/clusterbuilder.go Signed-off-by: Natalie Arellano --- .../clusterbuilder/clusterbuilder.go | 20 ++++++++- .../clusterbuilder/clusterbuilder_test.go | 42 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index 84c2a4d56..7b2f7607f 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -74,6 +74,7 @@ func NewController( ClusterStoreLister: clusterStoreInformer.Lister(), ClusterBuildpackLister: clusterBuildpackInformer.Lister(), ClusterStackLister: clusterStackInformer.Lister(), + ClusterLifecycleLister: clusterLifecycleInformer.Lister(), SecretFetcher: secretFetcher, } @@ -122,6 +123,7 @@ type Reconciler struct { ClusterStoreLister buildlisters.ClusterStoreLister ClusterBuildpackLister buildlisters.ClusterBuildpackLister ClusterStackLister buildlisters.ClusterStackLister + ClusterLifecycleLister buildlisters.ClusterLifecycleLister SecretFetcher Fetcher } @@ -168,6 +170,17 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu }, }, builder.NamespacedName()) + c.Tracker.Track(reconciler.Key{ + NamespacedName: types.NamespacedName{ + Name: builder.Spec.Lifecycle.Name, + Namespace: corev1.NamespaceAll, + }, + GroupKind: schema.GroupKind{ + Group: "kpack.io", + Kind: buildapi.ClusterLifecycleKind, + }, + }, builder.NamespacedName()) + var ( clusterStore *buildapi.ClusterStore err error @@ -209,6 +222,11 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu return buildapi.BuilderRecord{}, errors.Errorf("stack %s is not ready", clusterStack.Name) } + clusterLifecycle, err := c.ClusterLifecycleLister.Get(builder.Spec.Lifecycle.Name) + if err != nil { + return buildapi.BuilderRecord{}, err + } + builderKeychain, err := c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ ServiceAccount: builder.Spec.ServiceAccountRef.Name, Namespace: builder.Spec.ServiceAccountRef.Namespace, @@ -246,7 +264,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu stackKeychain, fetcher, clusterStack, - nil, // TODO: fix + clusterLifecycle, builder.Spec.BuilderSpec, serviceAccountSecrets, resolvedBuilderRef, diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder_test.go b/pkg/reconciler/clusterbuilder/clusterbuilder_test.go index d8c1c0032..f56727cd9 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder_test.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder_test.go @@ -65,6 +65,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { ClusterStoreLister: listers.GetClusterStoreLister(), ClusterBuildpackLister: listers.GetClusterBuildpackLister(), ClusterStackLister: listers.GetClusterStackLister(), + ClusterLifecycleLister: listers.GetClusterLifecycleLister(), SecretFetcher: fakeSecretFetcher, } return &kreconciler.NetworkErrorReconciler{Reconciler: r}, rtesting.ActionRecorderList{fakeClient}, rtesting.EventList{Recorder: record.NewFakeRecorder(10)} @@ -128,6 +129,33 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { }, } + clusterLifecycle := &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "some-lifecycle", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterLifecycle", + APIVersion: "kpack.io/v1alpha2", + }, + Spec: buildapi.ClusterLifecycleSpec{ + ServiceAccountRef: &corev1.ObjectReference{ + Namespace: "some-sa-namespace", + Name: "some-sa-name", + }, + }, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 0, + Conditions: []corev1alpha1.Condition{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + }, + }, + }, + } + clusterBuildpack := &buildapi.ClusterBuildpack{ ObjectMeta: metav1.ObjectMeta{ Name: "buildpack.id.4", @@ -153,6 +181,10 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { Kind: "Stack", Name: "some-stack", }, + Lifecycle: corev1.ObjectReference{ + Kind: "Lifecycle", + Name: "some-lifecycle", + }, Store: corev1.ObjectReference{ Kind: "ClusterStore", Name: "some-store", @@ -264,6 +296,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -285,6 +318,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { StackKeychain: ®istryfakes.FakeKeychain{}, Fetcher: expectedFetcher, ClusterStack: clusterStack, + ClusterLifecycle: clusterLifecycle, BuilderSpec: builder.Spec.BuilderSpec, SigningSecrets: []*corev1.Secret{}, ResolvedBuilderTag: expectedResolvedTag, @@ -331,6 +365,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, expectedBuilder, @@ -344,6 +379,8 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { kreconciler.KeyForObject(clusterStore), expectedBuilder.NamespacedName())) require.True(t, fakeTracker.IsTracking( kreconciler.KeyForObject(clusterStack), builder.NamespacedName())) + require.True(t, fakeTracker.IsTracking( + kreconciler.KeyForObject(clusterLifecycle), builder.NamespacedName())) require.True(t, fakeTracker.IsTrackingKind( kreconciler.KeyForObject(clusterBuildpack).GroupKind, builder.NamespacedName())) @@ -394,6 +431,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -435,6 +473,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -610,6 +649,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -653,6 +693,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, @@ -695,6 +736,7 @@ func testClusterBuilderReconciler(t *testing.T, when spec.G, it spec.S) { rt.Test(rtesting.TableRow{ Key: builderKey, Objects: []runtime.Object{ + clusterLifecycle, clusterStack, clusterStore, builder, From 6ba20f8b17ab7b8f442dfe071420191faed4c2bb Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 12:01:58 -0400 Subject: [PATCH 18/39] Add TODO Signed-off-by: Natalie Arellano --- pkg/reconciler/clusterbuilder/clusterbuilder.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index 7b2f7607f..5cc6fa006 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -108,6 +108,12 @@ func NewController( c.Tracker.OnChanged, buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterStackKind)), )) + // TODO: how is this tested? + //clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( + // controller.EnsureTypeMeta( + // c.Tracker.OnChanged, + // buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), + //)) return impl, func() { impl.GlobalResync(clusterBuilderInformer.Informer()) From 873fd06d9b58b0ddec25f83c6ad8258b7bfbd096 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 12:10:54 -0400 Subject: [PATCH 19/39] Fix pkg/cnb/create_builder_test.go Signed-off-by: Natalie Arellano --- pkg/cnb/create_builder_test.go | 46 ++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index cb3268aff..8105b6e07 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -46,6 +46,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { relocatedRunImageTag = "custom/example:test-builder-run-image" buildImageTag = "paketo-buildpacks/build:full-cnb" runImageTag = "paketo-buildpacks/run:full-cnb" + lifecycleImageTag = "buildpacksio/lifecycle:latest" buildImageLayers = 10 lifecycleImageLayers = 1 @@ -129,6 +130,25 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } + clusterLifecycle = &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-stack", + }, + Spec: buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{Image: lifecycleImageTag}, + }, + Status: buildapi.ClusterLifecycleStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: 11, + }, + ResolvedClusterLifecycle: buildapi.ResolvedClusterLifecycle{ + Version: "some-version", + BuildpackAPIs: []string{"some-buildpack-api"}, + PlatformAPIs: []string{"some-platform-api"}, + }, + }, + } + clusterBuilderSpec = buildapi.BuilderSpec{ Tag: builderTag, Stack: corev1.ObjectReference{ @@ -359,7 +379,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("creates a custom builder with a relocated run image", func() { - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) assert.Len(t, builderRecord.Buildpacks, 4) @@ -660,11 +680,11 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("creates images deterministically ", func() { - original, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + original, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) for i := 1; i <= 50; i++ { - other, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + other, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) @@ -695,7 +715,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.stack@v4: stack io.buildpacks.stacks.some-stack is not supported") }) @@ -719,7 +739,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.mixin@v4: stack missing mixin(s): something-missing-mixin, something-missing-mixin2") }) @@ -764,7 +784,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.Nil(t, err) }) @@ -789,7 +809,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, nil, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, nil, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.Error(t, err, "validating buildpack io.buildpack.relaxed.old.mixin@v4: stack missing mixin(s): build:common-mixin, run:common-mixin, another-common-mixin") }) @@ -812,7 +832,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.buildpack.api@v4: unsupported buildpack api: 0.1, expecting: 0.2, 0.3") }) @@ -855,7 +875,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) }) }) @@ -882,14 +902,14 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "unsupported platform apis in kpack lifecycle: 0.1, 0.2, 0.999, expecting one of: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8") }) }) when("signing a builder image", func() { it("does not populate the signature paths when no secrets were present", func() { - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) require.NotNil(t, builderRecord) require.Empty(t, builderRecord.SignaturePaths) @@ -909,7 +929,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) require.Error(t, err) }) @@ -932,7 +952,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) require.NoError(t, err) require.NotNil(t, builderRecord) require.NotEmpty(t, builderRecord.SignaturePaths) From ef9cff5133d8f8a3ca6806b353f9b9da8a40aef4 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 12:26:55 -0400 Subject: [PATCH 20/39] WIP Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 25 +-- pkg/cnb/create_builder.go | 21 +- pkg/cnb/create_builder_test.go | 196 ++++++++---------- pkg/config/lifecycle_provider.go | 2 + pkg/reconciler/builder/builder.go | 2 +- .../clusterbuilder/clusterbuilder.go | 2 +- pkg/slsa/attest.go | 10 +- 7 files changed, 106 insertions(+), 152 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index d8958d2f6..a9795b84d 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -55,7 +55,6 @@ import ( "github.com/pivotal/kpack/pkg/reconciler/clusterstack" "github.com/pivotal/kpack/pkg/reconciler/clusterstore" "github.com/pivotal/kpack/pkg/reconciler/image" - "github.com/pivotal/kpack/pkg/reconciler/lifecycle" "github.com/pivotal/kpack/pkg/reconciler/sourceresolver" "github.com/pivotal/kpack/pkg/registry" "github.com/pivotal/kpack/pkg/secret" @@ -196,14 +195,11 @@ func main() { RegistryClient: ®istry.Client{}, } - lifecycleProvider := config.NewLifecycleProvider(®istry.Client{}, keychainFactory) - builderCreator := &cnb.RemoteBuilderCreator{ - RegistryClient: ®istry.Client{}, - KpackVersion: cmd.Identifer, - LifecycleProvider: lifecycleProvider, - KeychainFactory: keychainFactory, - ImageSigner: cosign.NewImageSigner(sign.SignCmd, ociremote.SignatureTag), + RegistryClient: ®istry.Client{}, + KpackVersion: cmd.Identifer, + KeychainFactory: keychainFactory, + ImageSigner: cosign.NewImageSigner(sign.SignCmd, ociremote.SignatureTag), } podProgressLogger := &buildchange.ProgressLogger{ @@ -213,8 +209,7 @@ func main() { slsaAttester := slsa.Attester{ Version: cmd.Version, - LifecycleProvider: lifecycleProvider, - ImageReader: slsa.NewImageReader(®istry.Client{}), + ImageReader: slsa.NewImageReader(®istry.Client{}), Images: images, Features: featureFlags, @@ -230,17 +225,12 @@ func main() { buildController := build.NewController(ctx, options, k8sClient, buildInformer, podInformer, metadataRetriever, buildpodGenerator, podProgressLogger, keychainFactory, &slsaAttester, secretFetcher, featureFlags) imageController := image.NewController(ctx, options, k8sClient, imageInformer, buildInformer, duckBuilderInformer, sourceResolverInformer, pvcInformer, cfg.EnablePriorityClasses) sourceResolverController := sourceresolver.NewController(ctx, options, sourceResolverInformer, gitResolver, blobResolver, registryResolver) - builderController, builderResync := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) + builderController, _ := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) buildpackController := buildpack.NewController(ctx, options, keychainFactory, buildpackInformer, remoteStoreReader) - clusterBuilderController, clusterBuilderResync := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) + clusterBuilderController, _ := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) clusterBuildpackController := clusterbuildpack.NewController(ctx, options, keychainFactory, clusterBuildpackInformer, remoteStoreReader) clusterStoreController := clusterstore.NewController(ctx, options, keychainFactory, clusterStoreInformer, remoteStoreReader) clusterStackController := clusterstack.NewController(ctx, options, keychainFactory, clusterStackInformer, remoteStackReader) - // TODO: should this be renamed to clusterLifecycleController? Should we create a new controller? - lifecycleController := lifecycle.NewController(ctx, options, k8sClient, config.LifecycleConfigName, lifecycleConfigmapInformer, lifecycleProvider) - - lifecycleProvider.AddEventHandler(builderResync) - lifecycleProvider.AddEventHandler(clusterBuilderResync) stopChan := make(chan struct{}) informerFactory.Start(stopChan) @@ -272,7 +262,6 @@ func main() { run(clusterBuilderController, routinesPerController), run(clusterBuildpackController, routinesPerController), run(clusterStoreController, routinesPerController), - run(lifecycleController, routinesPerController), run(sourceResolverController, 2*routinesPerController), func(ctx context.Context) error { return configMapWatcher.Start(ctx.Done()) diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 911d7324a..a5dd38d70 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -24,11 +24,10 @@ type LifecycleProvider interface { } type RemoteBuilderCreator struct { - RegistryClient RegistryClient - LifecycleProvider LifecycleProvider - KpackVersion string - KeychainFactory registry.KeychainFactory - ImageSigner cosign.BuilderSigner + RegistryClient RegistryClient + KpackVersion string + KeychainFactory registry.KeychainFactory + ImageSigner cosign.BuilderSigner } func (r *RemoteBuilderCreator) CreateBuilder( @@ -64,12 +63,12 @@ func (r *RemoteBuilderCreator) CreateBuilder( return buildapi.BuilderRecord{}, err } - lifecycleLayer, lifecycleMetadata, err := r.LifecycleProvider.LayerForOS(builderBldr.os) - if err != nil { - return buildapi.BuilderRecord{}, err - } - - builderBldr.AddLifecycle(lifecycleLayer, lifecycleMetadata) + // TODO: we need to get the lifecycleLayer & lifecycleMetadata from the clusterLifecycle + //lifecycleLayer, lifecycleMetadata, err := r.LifecycleProvider.LayerForOS(builderBldr.os) + //if err != nil { + // return buildapi.BuilderRecord{}, err + //} + //builderBldr.AddLifecycle(lifecycleLayer, lifecycleMetadata) for _, group := range spec.Order { buildpacks := make([]RemoteBuildpackRef, 0, len(group.Group)) diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 8105b6e07..0b8b4ad73 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -196,13 +196,10 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - lifecycleProvider = &fakeLifecycleProvider{} - subject = RemoteBuilderCreator{ - RegistryClient: registryClient, - KpackVersion: "v1.2.3 (git sha: abcdefg123456)", - KeychainFactory: keychainFactory, - LifecycleProvider: lifecycleProvider, + RegistryClient: registryClient, + KpackVersion: "v1.2.3 (git sha: abcdefg123456)", + KeychainFactory: keychainFactory, ImageSigner: &fakeBuilderSigner{ signBuilder: func(ctx context.Context, s string, secrets []*corev1.Secret, keychain authn.Keychain) ([]buildapi.CosignSignature, error) { // no-op @@ -353,29 +350,29 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { registryClient.AddImage(buildImage, buildImg, stackKeychain) - lifecycleProvider.metadata = LifecycleMetadata{ - LifecycleInfo: LifecycleInfo{ - Version: "0.5.0", - }, - API: LifecycleAPI{ - BuildpackVersion: "0.2", - PlatformVersion: "0.1", - }, - APIs: LifecycleAPIs{ - Buildpack: APIVersions{ - Deprecated: []string{"0.2"}, - Supported: []string{"0.3"}, - }, - Platform: APIVersions{ - Deprecated: []string{"0.3"}, - Supported: []string{"0.4"}, - }, - }, - } - lifecycleProvider.layers = map[string]v1.Layer{ - "linux": linuxLifecycle, - "windows": windowsLifecycle, - } + //lifecycleProvider.metadata = LifecycleMetadata{ + // LifecycleInfo: LifecycleInfo{ + // Version: "0.5.0", + // }, + // API: LifecycleAPI{ + // BuildpackVersion: "0.2", + // PlatformVersion: "0.1", + // }, + // APIs: LifecycleAPIs{ + // Buildpack: APIVersions{ + // Deprecated: []string{"0.2"}, + // Supported: []string{"0.3"}, + // }, + // Platform: APIVersions{ + // Deprecated: []string{"0.3"}, + // Supported: []string{"0.4"}, + // }, + // }, + //} + //lifecycleProvider.layers = map[string]v1.Layer{ + // "linux": linuxLifecycle, + // "windows": windowsLifecycle, + //} }) it("creates a custom builder with a relocated run image", func() { @@ -744,25 +741,25 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("works with relaxed mixin contract", func() { - lifecycleProvider.metadata = LifecycleMetadata{ - LifecycleInfo: LifecycleInfo{ - Version: "0.5.0", - }, - API: LifecycleAPI{ - BuildpackVersion: "0.2", - PlatformVersion: "0.7", - }, - APIs: LifecycleAPIs{ - Buildpack: APIVersions{ - Deprecated: []string{"0.2"}, - Supported: []string{"0.3"}, - }, - Platform: APIVersions{ - Deprecated: []string{}, - Supported: []string{relaxedMixinMinPlatformAPI}, - }, - }, - } + //lifecycleProvider.metadata = LifecycleMetadata{ + // LifecycleInfo: LifecycleInfo{ + // Version: "0.5.0", + // }, + // API: LifecycleAPI{ + // BuildpackVersion: "0.2", + // PlatformVersion: "0.7", + // }, + // APIs: LifecycleAPIs{ + // Buildpack: APIVersions{ + // Deprecated: []string{"0.2"}, + // Supported: []string{"0.3"}, + // }, + // Platform: APIVersions{ + // Deprecated: []string{}, + // Supported: []string{relaxedMixinMinPlatformAPI}, + // }, + // }, + //} addBuildpack(t, "io.buildpack.relaxed.mixin", "v4", "buildpack.1.com", "0.2", []corev1alpha1.BuildpackStack{ @@ -837,25 +834,25 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("supports anystack buildpacks", func() { - lifecycleProvider.metadata = LifecycleMetadata{ - LifecycleInfo: LifecycleInfo{ - Version: "0.5.0", - }, - API: LifecycleAPI{ - BuildpackVersion: "0.2", - PlatformVersion: "0.1", - }, - APIs: LifecycleAPIs{ - Buildpack: APIVersions{ - Deprecated: []string{"0.2"}, - Supported: []string{"0.3", "0.4", "0.5"}, - }, - Platform: APIVersions{ - Deprecated: []string{"0.3"}, - Supported: []string{"0.4"}, - }, - }, - } + //lifecycleProvider.metadata = LifecycleMetadata{ + // LifecycleInfo: LifecycleInfo{ + // Version: "0.5.0", + // }, + // API: LifecycleAPI{ + // BuildpackVersion: "0.2", + // PlatformVersion: "0.1", + // }, + // APIs: LifecycleAPIs{ + // Buildpack: APIVersions{ + // Deprecated: []string{"0.2"}, + // Supported: []string{"0.3", "0.4", "0.5"}, + // }, + // Platform: APIVersions{ + // Deprecated: []string{"0.3"}, + // Supported: []string{"0.4"}, + // }, + // }, + //} addBuildpack(t, "anystack.buildpack", "v1", "buildpacks.com", "0.5", []corev1alpha1.BuildpackStack{ @@ -882,25 +879,25 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { when("validating platform api", func() { it("errors if no lifecycle platform api is supported", func() { - lifecycleProvider.metadata = LifecycleMetadata{ - LifecycleInfo: LifecycleInfo{ - Version: "0.5.0", - }, - API: LifecycleAPI{ - BuildpackVersion: "0.2", - PlatformVersion: "0.1", - }, - APIs: LifecycleAPIs{ - Buildpack: APIVersions{ - Deprecated: []string{"0.2"}, - Supported: []string{"0.3"}, - }, - Platform: APIVersions{ - Deprecated: []string{"0.1"}, - Supported: []string{"0.2", "0.999"}, - }, - }, - } + //lifecycleProvider.metadata = LifecycleMetadata{ + // LifecycleInfo: LifecycleInfo{ + // Version: "0.5.0", + // }, + // API: LifecycleAPI{ + // BuildpackVersion: "0.2", + // PlatformVersion: "0.1", + // }, + // APIs: LifecycleAPIs{ + // Buildpack: APIVersions{ + // Deprecated: []string{"0.2"}, + // Supported: []string{"0.3"}, + // }, + // Platform: APIVersions{ + // Deprecated: []string{"0.1"}, + // Supported: []string{"0.2", "0.999"}, + // }, + // }, + //} _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "unsupported platform apis in kpack lifecycle: 0.1, 0.2, 0.999, expecting one of: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8") @@ -969,15 +966,6 @@ func (s *fakeBuilderSigner) SignBuilder(ctx context.Context, imageReference stri return s.signBuilder(ctx, imageReference, signingSecrets, builderKeychain) } -type fakeLifecycleProvider struct { - metadata LifecycleMetadata - layers map[string]v1.Layer -} - -func (p *fakeLifecycleProvider) LayerForOS(os string) (v1.Layer, LifecycleMetadata, error) { - return p.layers[os], p.metadata, nil -} - func buildpackInfoInLayers(buildpackLayers []buildpackLayer, id, version string) DescriptiveBuildpackInfo { for _, b := range buildpackLayers { if b.BuildpackInfo.Id == id && b.BuildpackInfo.Version == version { @@ -1083,24 +1071,6 @@ func (i *layerIteratorTester) testNextLayer(name string, test func(index int)) { *i++ } -func layerToRemoteBuildpack(bpLayer buildpackLayer, layer *fakeLayer, secretRef registry.SecretRef) K8sRemoteBuildpack { - return K8sRemoteBuildpack{ - Buildpack: corev1alpha1.BuildpackStatus{ - BuildpackInfo: corev1alpha1.BuildpackInfo{ - Id: bpLayer.BuildpackInfo.Id, - Version: bpLayer.BuildpackInfo.Version, - }, - DiffId: layer.diffID, - Digest: layer.digest, - Size: layer.size, - Homepage: bpLayer.BuildpackInfo.Homepage, - API: bpLayer.BuildpackLayerInfo.API, - Stacks: bpLayer.BuildpackLayerInfo.Stacks, - }, - SecretRef: secretRef, - } -} - func createRunImage(os string) v1.Image { runImg, _ := random.Image(1, int64(5)) diff --git a/pkg/config/lifecycle_provider.go b/pkg/config/lifecycle_provider.go index 2a75ab5c6..dd04cbd61 100644 --- a/pkg/config/lifecycle_provider.go +++ b/pkg/config/lifecycle_provider.go @@ -1,5 +1,7 @@ package config +// TODO: pkg/config/lifecycle_provider.go can probably just be removed? + import ( "context" "sync/atomic" diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 87afaa2e5..711ec7a59 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -123,7 +123,7 @@ func NewController( )) return impl, func() { - impl.GlobalResync(builderInformer.Informer()) + impl.GlobalResync(builderInformer.Informer()) // TODO: can we remove this part? } } diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index 5cc6fa006..d935c9a73 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -116,7 +116,7 @@ func NewController( //)) return impl, func() { - impl.GlobalResync(clusterBuilderInformer.Informer()) + impl.GlobalResync(clusterBuilderInformer.Informer()) // TODO: can we remove this part? } } diff --git a/pkg/slsa/attest.go b/pkg/slsa/attest.go index 86242e9bd..5ba30ee4d 100644 --- a/pkg/slsa/attest.go +++ b/pkg/slsa/attest.go @@ -35,8 +35,7 @@ type ImageReader interface { type Attester struct { Version string - ImageReader ImageReader - LifecycleProvider LifecycleProvider + ImageReader ImageReader Images config.Images Features config.FeatureFlags @@ -61,11 +60,6 @@ func (a *Attester) AttestBuild(build *buildv1alpha2.Build, buildMetadata *cnb.Bu start, stop := getStartStopTime(pod) - lifecycle, err := a.LifecycleProvider.Metadata() - if err != nil { - return intoto.Statement{}, fmt.Errorf("failed to read lifecycle metadata: %v", err) - } - builderDeps := make([]slsav1.ResourceDescriptor, 0) for i, fn := range depFns { dep, err := fn() @@ -102,7 +96,7 @@ func (a *Attester) AttestBuild(build *buildv1alpha2.Build, buildMetadata *cnb.Bu ID: string(builderId), Version: map[string]string{ "kpack": a.Version, - "lifecycle": lifecycle.Version, + "lifecycle": "", // TODO: we need to get the lifecycle version from the build metadata }, BuilderDependencies: builderDeps, }, From 64d07497ead6d1d3142ce9f11aec00f62181b704 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 16:29:46 -0400 Subject: [PATCH 21/39] Passing units for pkg/cnb/create_builder.go Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 7 +- .../build/v1alpha2/cluster_lifecycle_types.go | 47 +++- pkg/cnb/create_builder.go | 95 +++++-- pkg/cnb/create_builder_test.go | 243 ++++++++++-------- 4 files changed, 259 insertions(+), 133 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index a9795b84d..1a18f5c0a 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -196,10 +196,9 @@ func main() { } builderCreator := &cnb.RemoteBuilderCreator{ - RegistryClient: ®istry.Client{}, - KpackVersion: cmd.Identifer, - KeychainFactory: keychainFactory, - ImageSigner: cosign.NewImageSigner(sign.SignCmd, ociremote.SignatureTag), + RegistryClient: ®istry.Client{}, + KpackVersion: cmd.Identifer, + ImageSigner: cosign.NewImageSigner(sign.SignCmd, ociremote.SignatureTag), } podProgressLogger := &buildchange.ProgressLogger{ diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index fd547423d..f214fb381 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -41,13 +41,50 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { - // TODO: should the unique ID be the image digest reference? - // Id string `json:"id,omitempty"` - Version string `json:"version,omitempty"` - BuildpackAPIs []string `json:"buildpackAPIs,omitempty"` - PlatformAPIs []string `json:"platformAPIs,omitempty"` + Id string `json:"id,omitempty"` // TODO: should this be LatestImage? + Version string `json:"version,omitempty"` // TODO: remove + BuildpackAPIs []string `json:"buildpackAPIs,omitempty"` // TODO: remove + PlatformAPIs []string `json:"platformAPIs,omitempty"` // TODO: remove + + LifecycleInfo + + // Deprecated: Use `LifecycleAPIs` instead + API LifecycleAPI `json:"api,omitempty"` + APIs LifecycleAPIs `json:"apis,omitempty"` +} + +type LifecycleMetadata struct { +} + +type LifecycleDescriptor struct { + Info LifecycleInfo `toml:"lifecycle"` + + // Deprecated: Use `LifecycleAPIs` instead + API LifecycleAPI `toml:"api" json:"api,omitempty"` + APIs LifecycleAPIs `toml:"apis" json:"apis,omitempty"` +} + +type LifecycleInfo struct { + Version string `toml:"version" json:"version"` +} + +type LifecycleAPI struct { + BuildpackVersion string `toml:"buildpack" json:"buildpack,omitempty"` + PlatformVersion string `toml:"platform" json:"platform,omitempty"` +} + +type LifecycleAPIs struct { + Buildpack APIVersions `toml:"buildpack" json:"buildpack"` + Platform APIVersions `toml:"platform" json:"platform"` +} + +type APIVersions struct { + Deprecated APISet `toml:"deprecated" json:"deprecated"` + Supported APISet `toml:"supported" json:"supported"` } +type APISet []string + // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:openapi-gen=true diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index a5dd38d70..bf0c71952 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -2,6 +2,7 @@ package cnb import ( "context" + "errors" "fmt" "github.com/google/go-containerregistry/pkg/authn" @@ -11,7 +12,6 @@ import ( buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" "github.com/pivotal/kpack/pkg/cosign" - "github.com/pivotal/kpack/pkg/registry" ) type RegistryClient interface { @@ -19,15 +19,10 @@ type RegistryClient interface { Save(keychain authn.Keychain, tag string, image ggcrv1.Image) (string, error) } -type LifecycleProvider interface { - LayerForOS(os string) (ggcrv1.Layer, LifecycleMetadata, error) -} - type RemoteBuilderCreator struct { - RegistryClient RegistryClient - KpackVersion string - KeychainFactory registry.KeychainFactory - ImageSigner cosign.BuilderSigner + RegistryClient RegistryClient + KpackVersion string + ImageSigner cosign.BuilderSigner } func (r *RemoteBuilderCreator) CreateBuilder( @@ -49,6 +44,10 @@ func (r *RemoteBuilderCreator) CreateBuilder( if err != nil { return buildapi.BuilderRecord{}, err } + lifecycleImage, _, err := r.RegistryClient.Fetch(stackKeychain, clusterLifecycle.Status.ResolvedClusterLifecycle.Id) // TODO: use lifecycle keychain + if err != nil { + return buildapi.BuilderRecord{}, err + } builderBldr := newBuilderBldr(r.KpackVersion) @@ -63,12 +62,11 @@ func (r *RemoteBuilderCreator) CreateBuilder( return buildapi.BuilderRecord{}, err } - // TODO: we need to get the lifecycleLayer & lifecycleMetadata from the clusterLifecycle - //lifecycleLayer, lifecycleMetadata, err := r.LifecycleProvider.LayerForOS(builderBldr.os) - //if err != nil { - // return buildapi.BuilderRecord{}, err - //} - //builderBldr.AddLifecycle(lifecycleLayer, lifecycleMetadata) + lifecycleLayer, lifecycleMetadata, err := layerForOS(clusterLifecycle, lifecycleImage, builderBldr.os) + if err != nil { + return buildapi.BuilderRecord{}, err + } + builderBldr.AddLifecycle(lifecycleLayer, lifecycleMetadata) for _, group := range spec.Order { buildpacks := make([]RemoteBuildpackRef, 0, len(group.Group)) @@ -129,6 +127,73 @@ func (r *RemoteBuilderCreator) CreateBuilder( return builder, nil } +func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcrv1.Image, os string) (lifecycleLayer ggcrv1.Layer, lifecycleMetadata LifecycleMetadata, err error) { + // TODO: do we really need to define LifecycleMetadata in this package? + lifecycleMetadata = LifecycleMetadata{ + LifecycleInfo: LifecycleInfo{ + Version: clusterLifecycle.Status.ResolvedClusterLifecycle.LifecycleInfo.Version, + }, + API: LifecycleAPI{ + BuildpackVersion: clusterLifecycle.Status.ResolvedClusterLifecycle.API.BuildpackVersion, + PlatformVersion: clusterLifecycle.Status.ResolvedClusterLifecycle.API.PlatformVersion, + }, + APIs: LifecycleAPIs{ + Buildpack: APIVersions{ + Deprecated: toCNBAPISet(clusterLifecycle.Status.ResolvedClusterLifecycle.APIs.Buildpack.Deprecated), + Supported: toCNBAPISet(clusterLifecycle.Status.ResolvedClusterLifecycle.APIs.Buildpack.Supported), + }, + Platform: APIVersions{ + Deprecated: toCNBAPISet(clusterLifecycle.Status.ResolvedClusterLifecycle.APIs.Platform.Deprecated), + Supported: toCNBAPISet(clusterLifecycle.Status.ResolvedClusterLifecycle.APIs.Platform.Supported), + }, + }, + } + + lifecycleLayer, err = func() (ggcrv1.Layer, error) { + manifest, err := lifecycleImage.Manifest() + if err != nil || manifest == nil { + return nil, fmt.Errorf("failed to get manifest file: %w", err) + } + if len(manifest.Layers) < 1 { + return nil, errors.New("failed to find lifecycle image layers") + } + lastLayerDigest := manifest.Layers[len(manifest.Layers)-1].Digest + return lifecycleImage.LayerByDigest(lastLayerDigest) + }() + if err != nil { + return nil, LifecycleMetadata{}, fmt.Errorf("failed to find lifecycle layer: %w", err) + } + + err = func() error { + cfg, err := lifecycleImage.ConfigFile() + if err != nil || cfg == nil { + return fmt.Errorf("failed to get config file: %w", err) + } + if cfg.OS != os { // TODO: check arch + return fmt.Errorf( + "validating lifecycle image %s: expected OS to be %s but got %s", + clusterLifecycle.Status.ResolvedClusterLifecycle.Id, + os, + cfg.OS, + ) + } + return nil + }() + if err != nil { + return nil, LifecycleMetadata{}, err + } + + return lifecycleLayer, lifecycleMetadata, nil +} + +func toCNBAPISet(from buildapi.APISet) APISet { + var to APISet + for _, f := range from { + to = append(to, f) + } + return to +} + func buildpackMetadata(buildpacks []DescriptiveBuildpackInfo) corev1alpha1.BuildpackMetadataList { m := make(corev1alpha1.BuildpackMetadataList, 0, len(buildpacks)) for _, b := range buildpacks { diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 0b8b4ad73..10ef46bba 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -21,7 +21,6 @@ import ( buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" - "github.com/pivotal/kpack/pkg/registry" "github.com/pivotal/kpack/pkg/registry/imagehelpers" "github.com/pivotal/kpack/pkg/registry/registryfakes" ) @@ -46,6 +45,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { relocatedRunImageTag = "custom/example:test-builder-run-image" buildImageTag = "paketo-buildpacks/build:full-cnb" runImageTag = "paketo-buildpacks/run:full-cnb" + lifecycleImage = "index.docker.io/buildpacksio/lifecycle@sha256:6dd262f754439ce98c901214934e587fd8a00be8b2b1cdf0b62cdade3d93379b" lifecycleImageTag = "buildpacksio/lifecycle:latest" buildImageLayers = 10 lifecycleImageLayers = 1 @@ -57,10 +57,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { var ( registryClient = registryfakes.NewFakeClient() - keychainFactory = ®istryfakes.FakeKeychainFactory{} builderKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) stackKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) - secretRef = registry.SecretRef{} runImage = createRunImage(os) runImageDigest = digest(runImage) runImageRef = fmt.Sprintf("%s@%s", runImageTag, runImageDigest) @@ -68,18 +66,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { fetcher = &fakeFetcher{buildpacks: map[string][]buildpackLayer{}, observedGeneration: 10} - linuxLifecycle = &fakeLayer{ - digest: "sha256:5d43d12dabe6070c4a4036e700a6f88a52278c02097b5f200e0b49b3d874c954", - diffID: "sha256:5d43d12dabe6070c4a4036e700a6f88a52278c02097b5f200e0b49b3d874c954", - size: 200, - } - - windowsLifecycle = &fakeLayer{ - digest: "sha256:e40a7455f5495621a585e68523ab66ad8a0b7c791f40bf3aa97c7858003c1287", - diffID: "sha256:e40a7455f5495621a585e68523ab66ad8a0b7c791f40bf3aa97c7858003c1287", - size: 200, - } - buildpack1Layer = &fakeLayer{ digest: "sha256:1bd8899667b8d1e6b124f663faca32903b470831e5e4e99265c839ab34628838", diffID: "sha256:1bf8899667b8d1e6b124f663faca32903b470831e5e4e992644ac5c839ab3462", @@ -197,9 +183,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { } subject = RemoteBuilderCreator{ - RegistryClient: registryClient, - KpackVersion: "v1.2.3 (git sha: abcdefg123456)", - KeychainFactory: keychainFactory, + RegistryClient: registryClient, + KpackVersion: "v1.2.3 (git sha: abcdefg123456)", ImageSigner: &fakeBuilderSigner{ signBuilder: func(ctx context.Context, s string, secrets []*corev1.Secret, keychain authn.Keychain) ([]buildapi.CosignSignature, error) { // no-op @@ -228,8 +213,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { ) it.Before(func() { - keychainFactory.AddKeychainForSecretRef(t, secretRef, builderKeychain) - buildpack1 := buildpackLayer{ v1Layer: buildpack1Layer, BuildpackInfo: DescriptiveBuildpackInfo{ @@ -250,7 +233,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, }, } - buildpack2 := buildpackLayer{ v1Layer: buildpack2Layer, BuildpackInfo: DescriptiveBuildpackInfo{ @@ -333,12 +315,15 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { when("CreateBuilder", func() { var ( - buildImg v1.Image + buildImg v1.Image + lifecycleImg v1.Image ) it.Before(func() { var err error + // build image + buildImg, err = random.Image(1, int64(buildImageLayers)) require.NoError(t, err) @@ -350,29 +335,41 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { registryClient.AddImage(buildImage, buildImg, stackKeychain) - //lifecycleProvider.metadata = LifecycleMetadata{ - // LifecycleInfo: LifecycleInfo{ - // Version: "0.5.0", - // }, - // API: LifecycleAPI{ - // BuildpackVersion: "0.2", - // PlatformVersion: "0.1", - // }, - // APIs: LifecycleAPIs{ - // Buildpack: APIVersions{ - // Deprecated: []string{"0.2"}, - // Supported: []string{"0.3"}, - // }, - // Platform: APIVersions{ - // Deprecated: []string{"0.3"}, - // Supported: []string{"0.4"}, - // }, - // }, - //} - //lifecycleProvider.layers = map[string]v1.Layer{ - // "linux": linuxLifecycle, - // "windows": windowsLifecycle, - //} + // lifecycle image + + lifecycleImg, err = random.Image(1, int64(lifecycleImageLayers)) + require.NoError(t, err) + + lConfig, err := lifecycleImg.ConfigFile() + require.NoError(t, err) + + lConfig.OS = os + lifecycleImg, err = mutate.ConfigFile(lifecycleImg, config) + + registryClient.AddImage(lifecycleImage, lifecycleImg, stackKeychain) + + // cluster lifecycle + + clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ + Id: lifecycleImage, + LifecycleInfo: buildapi.LifecycleInfo{ + Version: "0.5.0", + }, + API: buildapi.LifecycleAPI{ + BuildpackVersion: "0.2", + PlatformVersion: "0.1", + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{ + Deprecated: []string{"0.2"}, + Supported: []string{"0.3"}, + }, + Platform: buildapi.APIVersions{ + Deprecated: []string{"0.3"}, + Supported: []string{"0.4"}, + }, + }, + } }) it("creates a custom builder with a relocated run image", func() { @@ -484,13 +481,16 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) }) - layerTester.testNextLayer("Lifecycle Layer", func(index int) { - if os == "linux" { - assert.Equal(t, layers[index], linuxLifecycle) - } else { - assert.Equal(t, layers[index], windowsLifecycle) - } - }) + lifecycleImgManifest, err := lifecycleImg.Manifest() + require.NoError(t, err) + for i := 0; i < lifecycleImageLayers; i++ { + layerTester.testNextLayer("Lifecycle Layer", func(index int) { + lifecycleImgLayer, err := lifecycleImg.LayerByDigest(lifecycleImgManifest.Layers[i].Digest) + require.NoError(t, err) + + assert.Equal(t, layers[index+i], lifecycleImgLayer) + }) + } layerTester.testNextLayer("Largest Buildpack Layer", func(index int) { assert.Equal(t, layers[index], buildpack3Layer) @@ -690,6 +690,28 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { } }) + when("validating os", func() { + var wrongOS string + + it.Before(func() { + cfg, err := lifecycleImg.ConfigFile() + require.NoError(t, err) + wrongOS = "windows" + if os == "windows" { + wrongOS = "linux" + } + cfg.OS = wrongOS + lifecycleImg, err = mutate.ConfigFile(lifecycleImg, cfg) + require.NoError(t, err) + registryClient.AddImage(lifecycleImage, lifecycleImg, stackKeychain) + }) + + it("errors with unsupported os", func() { + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected OS to be %s but got %s", lifecycleImage, os, wrongOS)) + }) + }) + when("validating buildpacks", func() { it("errors with unsupported stack", func() { addBuildpack(t, "io.buildpack.unsupported.stack", "v4", "buildpack.4.com", "0.2", @@ -741,25 +763,26 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("works with relaxed mixin contract", func() { - //lifecycleProvider.metadata = LifecycleMetadata{ - // LifecycleInfo: LifecycleInfo{ - // Version: "0.5.0", - // }, - // API: LifecycleAPI{ - // BuildpackVersion: "0.2", - // PlatformVersion: "0.7", - // }, - // APIs: LifecycleAPIs{ - // Buildpack: APIVersions{ - // Deprecated: []string{"0.2"}, - // Supported: []string{"0.3"}, - // }, - // Platform: APIVersions{ - // Deprecated: []string{}, - // Supported: []string{relaxedMixinMinPlatformAPI}, - // }, - // }, - //} + clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ + Id: lifecycleImage, + LifecycleInfo: buildapi.LifecycleInfo{ + Version: "0.5.0", + }, + API: buildapi.LifecycleAPI{ + BuildpackVersion: "0.2", + PlatformVersion: "0.7", + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{ + Deprecated: []string{"0.2"}, + Supported: []string{"0.3"}, + }, + Platform: buildapi.APIVersions{ + Deprecated: []string{}, + Supported: []string{relaxedMixinMinPlatformAPI}, + }, + }, + } addBuildpack(t, "io.buildpack.relaxed.mixin", "v4", "buildpack.1.com", "0.2", []corev1alpha1.BuildpackStack{ @@ -834,25 +857,26 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("supports anystack buildpacks", func() { - //lifecycleProvider.metadata = LifecycleMetadata{ - // LifecycleInfo: LifecycleInfo{ - // Version: "0.5.0", - // }, - // API: LifecycleAPI{ - // BuildpackVersion: "0.2", - // PlatformVersion: "0.1", - // }, - // APIs: LifecycleAPIs{ - // Buildpack: APIVersions{ - // Deprecated: []string{"0.2"}, - // Supported: []string{"0.3", "0.4", "0.5"}, - // }, - // Platform: APIVersions{ - // Deprecated: []string{"0.3"}, - // Supported: []string{"0.4"}, - // }, - // }, - //} + clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ + Id: lifecycleImage, + LifecycleInfo: buildapi.LifecycleInfo{ + Version: "0.5.0", + }, + API: buildapi.LifecycleAPI{ + BuildpackVersion: "0.2", + PlatformVersion: "0.1", + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{ + Deprecated: []string{"0.2"}, + Supported: []string{"0.3", "0.4", "0.5"}, + }, + Platform: buildapi.APIVersions{ + Deprecated: []string{"0.3"}, + Supported: []string{"0.4"}, + }, + }, + } addBuildpack(t, "anystack.buildpack", "v1", "buildpacks.com", "0.5", []corev1alpha1.BuildpackStack{ @@ -879,25 +903,26 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { when("validating platform api", func() { it("errors if no lifecycle platform api is supported", func() { - //lifecycleProvider.metadata = LifecycleMetadata{ - // LifecycleInfo: LifecycleInfo{ - // Version: "0.5.0", - // }, - // API: LifecycleAPI{ - // BuildpackVersion: "0.2", - // PlatformVersion: "0.1", - // }, - // APIs: LifecycleAPIs{ - // Buildpack: APIVersions{ - // Deprecated: []string{"0.2"}, - // Supported: []string{"0.3"}, - // }, - // Platform: APIVersions{ - // Deprecated: []string{"0.1"}, - // Supported: []string{"0.2", "0.999"}, - // }, - // }, - //} + clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ + Id: lifecycleImage, + LifecycleInfo: buildapi.LifecycleInfo{ + Version: "0.5.0", + }, + API: buildapi.LifecycleAPI{ + BuildpackVersion: "0.2", + PlatformVersion: "0.1", + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{ + Deprecated: []string{"0.2"}, + Supported: []string{"0.3"}, + }, + Platform: buildapi.APIVersions{ + Deprecated: []string{"0.1"}, + Supported: []string{"0.2", "0.999"}, + }, + }, + } _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "unsupported platform apis in kpack lifecycle: 0.1, 0.2, 0.999, expecting one of: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8") From 33518b72e4345f0892da9da731a54ba7a83841e6 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 28 May 2024 16:29:55 -0400 Subject: [PATCH 22/39] Add TODO Signed-off-by: Natalie Arellano --- pkg/reconciler/clusterlifecycle/clusterlifecycle.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecycle.go b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go index 50f876a76..0edc2903c 100644 --- a/pkg/reconciler/clusterlifecycle/clusterlifecycle.go +++ b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go @@ -28,6 +28,7 @@ const ( //go:generate counterfeiter . ClusterLifecycleReader type ClusterLifecycleReader interface { + // TODO: add implementation Read(keychain authn.Keychain, clusterLifecycleSpec buildapi.ClusterLifecycleSpec) (buildapi.ResolvedClusterLifecycle, error) } From ba5a184c3c8331ca555438ff22c7d9524f8c1fcd Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 10:33:43 -0400 Subject: [PATCH 23/39] Update ResolvedClusterLifecycle struct and rerun codegen Signed-off-by: Natalie Arellano --- api/openapi-spec/swagger.json | 71 +++++++---- pkg/apis/build/v1alpha2/build_types.go | 17 ++- .../build/v1alpha2/cluster_lifecycle_types.go | 23 +--- .../build/v1alpha2/zz_generated.deepcopy.go | 96 ++++++++++++-- pkg/cnb/create_builder.go | 2 +- pkg/cnb/create_builder_test.go | 28 ++--- pkg/openapi/openapi_generated.go | 119 ++++++++++-------- .../clusterlifecycle/clusterlifecycle_test.go | 9 +- 8 files changed, 218 insertions(+), 147 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 5bbde89c9..c4fb658d0 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -5703,7 +5703,14 @@ }, "kpack.build.v1alpha2.BuildStatus": { "type": "object", + "required": [ + "Lifecycle" + ], "properties": { + "Lifecycle": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.ResolvedClusterLifecycle" + }, "buildMetadata": { "type": "array", "items": { @@ -5889,6 +5896,10 @@ "default": "" } }, + "lifecycle": { + "default": {}, + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, "order": { "type": "array", "items": { @@ -5933,6 +5944,10 @@ "latestImage": { "type": "string" }, + "lifecycle": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.ResolvedClusterLifecycle" + }, "observedGeneration": { "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", "type": "integer", @@ -6132,6 +6147,10 @@ "default": "" } }, + "lifecycle": { + "default": {}, + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, "order": { "type": "array", "items": { @@ -6324,12 +6343,14 @@ "kpack.build.v1alpha2.ClusterLifecycleStatus": { "type": "object", "properties": { - "buildpackAPIs": { - "type": "array", - "items": { - "type": "string", - "default": "" - } + "api": { + "description": "Deprecated: Use `LifecycleAPIs` instead", + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPI" + }, + "apis": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPIs" }, "conditions": { "description": "Conditions the latest available observations of a resource's current state.", @@ -6341,20 +6362,15 @@ "x-kubernetes-patch-merge-key": "type", "x-kubernetes-patch-strategy": "merge" }, + "id": { + "type": "string" + }, "observedGeneration": { "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", "type": "integer", "format": "int64" }, - "platformAPIs": { - "type": "array", - "items": { - "type": "string", - "default": "" - } - }, "version": { - "description": "Id string `json:\"id,omitempty\"`", "type": "string" } } @@ -6934,6 +6950,10 @@ "default": "" } }, + "lifecycle": { + "default": {}, + "$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference" + }, "order": { "type": "array", "items": { @@ -6976,22 +6996,19 @@ "kpack.build.v1alpha2.ResolvedClusterLifecycle": { "type": "object", "properties": { - "buildpackAPIs": { - "type": "array", - "items": { - "type": "string", - "default": "" - } + "api": { + "description": "Deprecated: Use `LifecycleAPIs` instead", + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPI" }, - "platformAPIs": { - "type": "array", - "items": { - "type": "string", - "default": "" - } + "apis": { + "default": {}, + "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPIs" + }, + "id": { + "type": "string" }, "version": { - "description": "Id string `json:\"id,omitempty\"`", "type": "string" } } diff --git a/pkg/apis/build/v1alpha2/build_types.go b/pkg/apis/build/v1alpha2/build_types.go index 53867bae4..0b231d71e 100644 --- a/pkg/apis/build/v1alpha2/build_types.go +++ b/pkg/apis/build/v1alpha2/build_types.go @@ -129,15 +129,14 @@ type BuildStack struct { // +k8s:openapi-gen=true type BuildStatus struct { - corev1alpha1.Status `json:",inline"` - BuildMetadata corev1alpha1.BuildpackMetadataList `json:"buildMetadata,omitempty"` - Stack corev1alpha1.BuildStack `json:"stack,omitempty"` - // TODO: confirm - Lifecycle ResolvedClusterLifecycle - LatestImage string `json:"latestImage,omitempty"` - LatestCacheImage string `json:"latestCacheImage,omitempty"` - LatestAttestationImage string `json:"latestAttestationImage,omitempty"` - PodName string `json:"podName,omitempty"` + corev1alpha1.Status `json:",inline"` + BuildMetadata corev1alpha1.BuildpackMetadataList `json:"buildMetadata,omitempty"` + Stack corev1alpha1.BuildStack `json:"stack,omitempty"` + Lifecycle ResolvedClusterLifecycle // TODO: confirm + LatestImage string `json:"latestImage,omitempty"` + LatestCacheImage string `json:"latestCacheImage,omitempty"` + LatestAttestationImage string `json:"latestAttestationImage,omitempty"` + PodName string `json:"podName,omitempty"` // +listType StepStates []corev1.ContainerState `json:"stepStates,omitempty"` // +listType diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index f214fb381..6567e9eaf 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -41,33 +41,14 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { - Id string `json:"id,omitempty"` // TODO: should this be LatestImage? - Version string `json:"version,omitempty"` // TODO: remove - BuildpackAPIs []string `json:"buildpackAPIs,omitempty"` // TODO: remove - PlatformAPIs []string `json:"platformAPIs,omitempty"` // TODO: remove - - LifecycleInfo + Id string `json:"id,omitempty"` // TODO: should this be LatestImage? + Version string `json:"version,omitempty"` // Deprecated: Use `LifecycleAPIs` instead API LifecycleAPI `json:"api,omitempty"` APIs LifecycleAPIs `json:"apis,omitempty"` } -type LifecycleMetadata struct { -} - -type LifecycleDescriptor struct { - Info LifecycleInfo `toml:"lifecycle"` - - // Deprecated: Use `LifecycleAPIs` instead - API LifecycleAPI `toml:"api" json:"api,omitempty"` - APIs LifecycleAPIs `toml:"apis" json:"apis,omitempty"` -} - -type LifecycleInfo struct { - Version string `toml:"version" json:"version"` -} - type LifecycleAPI struct { BuildpackVersion string `toml:"buildpack" json:"buildpack,omitempty"` PlatformVersion string `toml:"platform" json:"platform,omitempty"` diff --git a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go index 089b52226..e4f4725a5 100644 --- a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go @@ -28,6 +28,52 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in APISet) DeepCopyInto(out *APISet) { + { + in := &in + *out = make(APISet, len(*in)) + copy(*out, *in) + return + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APISet. +func (in APISet) DeepCopy() APISet { + if in == nil { + return nil + } + out := new(APISet) + in.DeepCopyInto(out) + return *out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIVersions) DeepCopyInto(out *APIVersions) { + *out = *in + if in.Deprecated != nil { + in, out := &in.Deprecated, &out.Deprecated + *out = make(APISet, len(*in)) + copy(*out, *in) + } + if in.Supported != nil { + in, out := &in.Supported, &out.Supported + *out = make(APISet, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIVersions. +func (in *APIVersions) DeepCopy() *APIVersions { + if in == nil { + return nil + } + out := new(APIVersions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Build) DeepCopyInto(out *Build) { *out = *in @@ -319,6 +365,7 @@ func (in *BuildStatus) DeepCopyInto(out *BuildStatus) { copy(*out, *in) } out.Stack = in.Stack + in.Lifecycle.DeepCopyInto(&out.Lifecycle) if in.StepStates != nil { in, out := &in.StepStates, &out.StepStates *out = make([]v1.ContainerState, len(*in)) @@ -456,6 +503,7 @@ func (in *BuilderOrderEntry) DeepCopy() *BuilderOrderEntry { func (in *BuilderRecord) DeepCopyInto(out *BuilderRecord) { *out = *in out.Stack = in.Stack + in.Lifecycle.DeepCopyInto(&out.Lifecycle) if in.Buildpacks != nil { in, out := &in.Buildpacks, &out.Buildpacks *out = make(v1alpha1.BuildpackMetadataList, len(*in)) @@ -490,6 +538,7 @@ func (in *BuilderRecord) DeepCopy() *BuilderRecord { func (in *BuilderSpec) DeepCopyInto(out *BuilderSpec) { *out = *in out.Stack = in.Stack + out.Lifecycle = in.Lifecycle out.Store = in.Store if in.Order != nil { in, out := &in.Order, &out.Order @@ -535,6 +584,7 @@ func (in *BuilderStatus) DeepCopyInto(out *BuilderStatus) { } } out.Stack = in.Stack + in.Lifecycle.DeepCopyInto(&out.Lifecycle) if in.SignaturePaths != nil { in, out := &in.SignaturePaths, &out.SignaturePaths *out = make([]CosignSignature, len(*in)) @@ -1565,6 +1615,40 @@ func (in *LastBuild) DeepCopy() *LastBuild { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LifecycleAPI) DeepCopyInto(out *LifecycleAPI) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LifecycleAPI. +func (in *LifecycleAPI) DeepCopy() *LifecycleAPI { + if in == nil { + return nil + } + out := new(LifecycleAPI) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LifecycleAPIs) DeepCopyInto(out *LifecycleAPIs) { + *out = *in + in.Buildpack.DeepCopyInto(&out.Buildpack) + in.Platform.DeepCopyInto(&out.Platform) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LifecycleAPIs. +func (in *LifecycleAPIs) DeepCopy() *LifecycleAPIs { + if in == nil { + return nil + } + out := new(LifecycleAPIs) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NamespacedBuilderSpec) DeepCopyInto(out *NamespacedBuilderSpec) { *out = *in @@ -1601,16 +1685,8 @@ func (in *RegistryCache) DeepCopy() *RegistryCache { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResolvedClusterLifecycle) DeepCopyInto(out *ResolvedClusterLifecycle) { *out = *in - if in.BuildpackAPIs != nil { - in, out := &in.BuildpackAPIs, &out.BuildpackAPIs - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.PlatformAPIs != nil { - in, out := &in.PlatformAPIs, &out.PlatformAPIs - *out = make([]string, len(*in)) - copy(*out, *in) - } + out.API = in.API + in.APIs.DeepCopyInto(&out.APIs) return } diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index bf0c71952..84916c0f2 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -131,7 +131,7 @@ func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcr // TODO: do we really need to define LifecycleMetadata in this package? lifecycleMetadata = LifecycleMetadata{ LifecycleInfo: LifecycleInfo{ - Version: clusterLifecycle.Status.ResolvedClusterLifecycle.LifecycleInfo.Version, + Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, }, API: LifecycleAPI{ BuildpackVersion: clusterLifecycle.Status.ResolvedClusterLifecycle.API.BuildpackVersion, diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 10ef46bba..37621b170 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -128,9 +128,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { ObservedGeneration: 11, }, ResolvedClusterLifecycle: buildapi.ResolvedClusterLifecycle{ - Version: "some-version", - BuildpackAPIs: []string{"some-buildpack-api"}, - PlatformAPIs: []string{"some-platform-api"}, + Version: "some-version", }, }, } @@ -351,10 +349,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { // cluster lifecycle clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, - LifecycleInfo: buildapi.LifecycleInfo{ - Version: "0.5.0", - }, + Id: lifecycleImage, + Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", PlatformVersion: "0.1", @@ -764,10 +760,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { it("works with relaxed mixin contract", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, - LifecycleInfo: buildapi.LifecycleInfo{ - Version: "0.5.0", - }, + Id: lifecycleImage, + Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", PlatformVersion: "0.7", @@ -858,10 +852,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { it("supports anystack buildpacks", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, - LifecycleInfo: buildapi.LifecycleInfo{ - Version: "0.5.0", - }, + Id: lifecycleImage, + Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", PlatformVersion: "0.1", @@ -904,10 +896,8 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { when("validating platform api", func() { it("errors if no lifecycle platform api is supported", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, - LifecycleInfo: buildapi.LifecycleInfo{ - Version: "0.5.0", - }, + Id: lifecycleImage, + Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", PlatformVersion: "0.1", diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index a14a05762..be77f4224 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -2389,6 +2389,12 @@ func schema_pkg_apis_build_v1alpha2_BuildStatus(ref common.ReferenceCallback) co Ref: ref("github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack"), }, }, + "Lifecycle": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle"), + }, + }, "latestImage": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -2451,10 +2457,11 @@ func schema_pkg_apis_build_v1alpha2_BuildStatus(ref common.ReferenceCallback) co }, }, }, + Required: []string{"Lifecycle"}, }, }, Dependencies: []string{ - "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "k8s.io/api/core/v1.ContainerState"}, + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "k8s.io/api/core/v1.ContainerState"}, } } @@ -2690,6 +2697,12 @@ func schema_pkg_apis_build_v1alpha2_BuilderSpec(ref common.ReferenceCallback) co Ref: ref("k8s.io/api/core/v1.ObjectReference"), }, }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, "store": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, @@ -2802,6 +2815,12 @@ func schema_pkg_apis_build_v1alpha2_BuilderStatus(ref common.ReferenceCallback) Ref: ref("github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack"), }, }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle"), + }, + }, "latestImage": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, @@ -2843,7 +2862,7 @@ func schema_pkg_apis_build_v1alpha2_BuilderStatus(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.CosignSignature", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.OrderEntry"}, + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.CosignSignature", "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.OrderEntry"}, } } @@ -3138,6 +3157,12 @@ func schema_pkg_apis_build_v1alpha2_ClusterBuilderSpec(ref common.ReferenceCallb Ref: ref("k8s.io/api/core/v1.ObjectReference"), }, }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, "store": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, @@ -3523,46 +3548,36 @@ func schema_pkg_apis_build_v1alpha2_ClusterLifecycleStatus(ref common.ReferenceC }, }, }, + "id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "version": { SchemaProps: spec.SchemaProps{ - Description: "Id string `json:\"id,omitempty\"`", - Type: []string{"string"}, - Format: "", + Type: []string{"string"}, + Format: "", }, }, - "buildpackAPIs": { + "api": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Description: "Deprecated: Use `LifecycleAPIs` instead", + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPI"), }, }, - "platformAPIs": { + "apis": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPIs"), }, }, }, }, }, Dependencies: []string{ - "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition"}, + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPI", "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPIs", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition"}, } } @@ -4660,6 +4675,12 @@ func schema_pkg_apis_build_v1alpha2_NamespacedBuilderSpec(ref common.ReferenceCa Ref: ref("k8s.io/api/core/v1.ObjectReference"), }, }, + "lifecycle": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ObjectReference"), + }, + }, "store": { SchemaProps: spec.SchemaProps{ Default: map[string]interface{}{}, @@ -4745,44 +4766,36 @@ func schema_pkg_apis_build_v1alpha2_ResolvedClusterLifecycle(ref common.Referenc SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ + "id": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, "version": { SchemaProps: spec.SchemaProps{ - Description: "Id string `json:\"id,omitempty\"`", - Type: []string{"string"}, - Format: "", + Type: []string{"string"}, + Format: "", }, }, - "buildpackAPIs": { + "api": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Description: "Deprecated: Use `LifecycleAPIs` instead", + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPI"), }, }, - "platformAPIs": { + "apis": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, + Default: map[string]interface{}{}, + Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPIs"), }, }, }, }, }, + Dependencies: []string{ + "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPI", "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.LifecycleAPIs"}, } } diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go b/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go index 88a2a5d0e..23f8c101d 100644 --- a/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go +++ b/pkg/reconciler/clusterlifecycle/clusterlifecycle_test.go @@ -71,9 +71,7 @@ func testClusterLifecycleReconciler(t *testing.T, when spec.G, it spec.S) { when("#Reconcile", func() { it("saves metadata to the status", func() { resolvedClusterLifecycle := buildapi.ResolvedClusterLifecycle{ - Version: "some-version", - BuildpackAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11"}, - PlatformAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, + Version: "some-version", } fakeClusterLifecycleReader.ReadReturns(resolvedClusterLifecycle, nil) emptySecretRef := registry.SecretRef{} @@ -115,9 +113,7 @@ func testClusterLifecycleReconciler(t *testing.T, when spec.G, it spec.S) { it("does not update the status with no status change", func() { resolvedClusterLifecycle := buildapi.ResolvedClusterLifecycle{ - Version: "some-version", - BuildpackAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11"}, - PlatformAPIs: []string{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}, + Version: "some-version", } fakeClusterLifecycleReader.ReadReturns(resolvedClusterLifecycle, nil) emptySecretRef := registry.SecretRef{} @@ -222,6 +218,5 @@ func testClusterLifecycleReconciler(t *testing.T, when spec.G, it spec.S) { actualKeyChain, _ := fakeClusterLifecycleReader.ReadArgsForCall(0) assert.Equal(t, expectedKeyChain, actualKeyChain) }) - }) } From ff88c9c01c1e1d1b20e9d1be204f5529fd3488db Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 11:01:41 -0400 Subject: [PATCH 24/39] Resolve some TODOs Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 4 +- pkg/apis/build/v1alpha2/builder_resource.go | 2 +- pkg/apis/build/v1alpha2/builder_types.go | 2 +- .../build/v1alpha2/cluster_lifecycle_types.go | 2 +- .../v1alpha2/cluster_lifecycle_validation.go | 25 ++++++++--- pkg/cnb/build_metadata.go | 2 +- pkg/cnb/create_builder.go | 4 +- pkg/cnb/create_builder_test.go | 45 ++++++++++--------- pkg/reconciler/builder/builder.go | 36 +++++++++------ .../clusterbuilder/clusterbuilder.go | 32 ++++++++----- .../testhelpers/fake_builder_creator.go | 1 + pkg/slsa/attest.go | 2 +- pkg/slsa/attest_test.go | 14 +----- 13 files changed, 97 insertions(+), 74 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 1a18f5c0a..f6f1948db 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -224,9 +224,9 @@ func main() { buildController := build.NewController(ctx, options, k8sClient, buildInformer, podInformer, metadataRetriever, buildpodGenerator, podProgressLogger, keychainFactory, &slsaAttester, secretFetcher, featureFlags) imageController := image.NewController(ctx, options, k8sClient, imageInformer, buildInformer, duckBuilderInformer, sourceResolverInformer, pvcInformer, cfg.EnablePriorityClasses) sourceResolverController := sourceresolver.NewController(ctx, options, sourceResolverInformer, gitResolver, blobResolver, registryResolver) - builderController, _ := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) + builderController := builder.NewController(ctx, options, builderInformer, builderCreator, keychainFactory, clusterStoreInformer, buildpackInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) buildpackController := buildpack.NewController(ctx, options, keychainFactory, buildpackInformer, remoteStoreReader) - clusterBuilderController, _ := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) + clusterBuilderController := clusterbuilder.NewController(ctx, options, clusterBuilderInformer, builderCreator, keychainFactory, clusterStoreInformer, clusterBuildpackInformer, clusterStackInformer, clusterLifecycleInformer, secretFetcher) clusterBuildpackController := clusterbuildpack.NewController(ctx, options, keychainFactory, clusterBuildpackInformer, remoteStoreReader) clusterStoreController := clusterstore.NewController(ctx, options, keychainFactory, clusterStoreInformer, remoteStoreReader) clusterStackController := clusterstack.NewController(ctx, options, keychainFactory, clusterStackInformer, remoteStackReader) diff --git a/pkg/apis/build/v1alpha2/builder_resource.go b/pkg/apis/build/v1alpha2/builder_resource.go index 2fe0d2ca2..ace3ee071 100644 --- a/pkg/apis/build/v1alpha2/builder_resource.go +++ b/pkg/apis/build/v1alpha2/builder_resource.go @@ -10,7 +10,7 @@ type BuilderResource interface { UpToDate() bool BuildpackMetadata() corev1alpha1.BuildpackMetadataList RunImage() string - // TODO: add LifecycleVersion here? + // TODO: confirm, should we add LifecycleVersion here? GetKind() string ConditionReadyMessage() string } diff --git a/pkg/apis/build/v1alpha2/builder_types.go b/pkg/apis/build/v1alpha2/builder_types.go index b236a5de8..c19edc246 100644 --- a/pkg/apis/build/v1alpha2/builder_types.go +++ b/pkg/apis/build/v1alpha2/builder_types.go @@ -34,7 +34,7 @@ type Builder struct { type BuilderSpec struct { Tag string `json:"tag,omitempty"` Stack corev1.ObjectReference `json:"stack,omitempty"` - // TODO: should `json:"lifecycle,...` actually be `json:"clusterlifecycle,...`? + // TODO: confirm, should `json:"lifecycle,...` actually be `json:"clusterlifecycle,...`? Lifecycle corev1.ObjectReference `json:"lifecycle,omitempty"` Store corev1.ObjectReference `json:"store,omitempty"` // +listType diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index 6567e9eaf..a554e15e3 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -41,7 +41,7 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { - Id string `json:"id,omitempty"` // TODO: should this be LatestImage? + Id string `json:"id,omitempty"` // TODO: confirm, should this be LatestImage? Version string `json:"version,omitempty"` // Deprecated: Use `LifecycleAPIs` instead diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go index c73a6557a..a94a11eb1 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_validation.go @@ -4,14 +4,27 @@ import ( "context" "knative.dev/pkg/apis" + + "github.com/pivotal/kpack/pkg/apis/validate" ) -func (s *ClusterLifecycle) SetDefaults(context.Context) { - // TODO +func (cl *ClusterLifecycle) SetDefaults(context.Context) { + // TODO: confirm, should we add something here? +} + +func (cl *ClusterLifecycle) Validate(ctx context.Context) *apis.FieldError { + return cl.Spec.Validate(ctx).ViaField("spec") } -func (s *ClusterLifecycle) Validate(ctx context.Context) *apis.FieldError { - //return s.Spec.Validate(ctx).ViaField("spec") - // TODO - return nil +func (cls *ClusterLifecycleSpec) Validate(ctx context.Context) *apis.FieldError { + if cls.ServiceAccountRef != nil { + if cls.ServiceAccountRef.Name == "" { + return apis.ErrMissingField("name").ViaField("serviceAccountRef") + } + if cls.ServiceAccountRef.Namespace == "" { + return apis.ErrMissingField("namespace").ViaField("serviceAccountRef") + } + } + + return validate.FieldNotEmpty(cls.Image, "image") } diff --git a/pkg/cnb/build_metadata.go b/pkg/cnb/build_metadata.go index 71e42ab6c..3e0f35496 100644 --- a/pkg/cnb/build_metadata.go +++ b/pkg/cnb/build_metadata.go @@ -25,7 +25,7 @@ type BuildMetadata struct { LatestImage string `json:"latestImage"` StackID string `json:"stackID"` StackRunImage string `json:"stackRunImage"` - // TODO: lifecycle git commit is also available + // TODO: confirm, lifecycle git commit is also available - do we want it? LifecycleVersion string `json:"lifecycleVersion"` } diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 84916c0f2..84f95a477 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -29,6 +29,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( ctx context.Context, builderKeychain authn.Keychain, stackKeychain authn.Keychain, + lifecycleKeychain authn.Keychain, fetcher RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, clusterLifecycle *buildapi.ClusterLifecycle, @@ -44,7 +45,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( if err != nil { return buildapi.BuilderRecord{}, err } - lifecycleImage, _, err := r.RegistryClient.Fetch(stackKeychain, clusterLifecycle.Status.ResolvedClusterLifecycle.Id) // TODO: use lifecycle keychain + lifecycleImage, _, err := r.RegistryClient.Fetch(lifecycleKeychain, clusterLifecycle.Status.ResolvedClusterLifecycle.Id) if err != nil { return buildapi.BuilderRecord{}, err } @@ -128,7 +129,6 @@ func (r *RemoteBuilderCreator) CreateBuilder( } func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcrv1.Image, os string) (lifecycleLayer ggcrv1.Layer, lifecycleMetadata LifecycleMetadata, err error) { - // TODO: do we really need to define LifecycleMetadata in this package? lifecycleMetadata = LifecycleMetadata{ LifecycleInfo: LifecycleInfo{ Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 37621b170..09cc90c59 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -57,12 +57,13 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { var ( registryClient = registryfakes.NewFakeClient() - builderKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) - stackKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) - runImage = createRunImage(os) - runImageDigest = digest(runImage) - runImageRef = fmt.Sprintf("%s@%s", runImageTag, runImageDigest) - ctx = context.Background() + builderKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) + stackKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) + lifecycleKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) + runImage = createRunImage(os) + runImageDigest = digest(runImage) + runImageRef = fmt.Sprintf("%s@%s", runImageTag, runImageDigest) + ctx = context.Background() fetcher = &fakeFetcher{buildpacks: map[string][]buildpackLayer{}, observedGeneration: 10} @@ -344,7 +345,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { lConfig.OS = os lifecycleImg, err = mutate.ConfigFile(lifecycleImg, config) - registryClient.AddImage(lifecycleImage, lifecycleImg, stackKeychain) + registryClient.AddImage(lifecycleImage, lifecycleImg, lifecycleKeychain) // cluster lifecycle @@ -369,7 +370,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("creates a custom builder with a relocated run image", func() { - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) assert.Len(t, builderRecord.Buildpacks, 4) @@ -673,11 +674,11 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }) it("creates images deterministically ", func() { - original, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + original, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) for i := 1; i <= 50; i++ { - other, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + other, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) @@ -699,11 +700,11 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { cfg.OS = wrongOS lifecycleImg, err = mutate.ConfigFile(lifecycleImg, cfg) require.NoError(t, err) - registryClient.AddImage(lifecycleImage, lifecycleImg, stackKeychain) + registryClient.AddImage(lifecycleImage, lifecycleImg, lifecycleKeychain) }) it("errors with unsupported os", func() { - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected OS to be %s but got %s", lifecycleImage, os, wrongOS)) }) }) @@ -730,7 +731,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.stack@v4: stack io.buildpacks.stacks.some-stack is not supported") }) @@ -754,7 +755,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.mixin@v4: stack missing mixin(s): something-missing-mixin, something-missing-mixin2") }) @@ -798,7 +799,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.Nil(t, err) }) @@ -823,7 +824,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, nil, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, nil, nil, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.Error(t, err, "validating buildpack io.buildpack.relaxed.old.mixin@v4: stack missing mixin(s): build:common-mixin, run:common-mixin, another-common-mixin") }) @@ -846,7 +847,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "validating buildpack io.buildpack.unsupported.buildpack.api@v4: unsupported buildpack api: 0.1, expecting: 0.2, 0.3") }) @@ -888,7 +889,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }}, }} - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) }) }) @@ -914,14 +915,14 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.EqualError(t, err, "unsupported platform apis in kpack lifecycle: 0.1, 0.2, 0.999, expecting one of: 0.3, 0.4, 0.5, 0.6, 0.7, 0.8") }) }) when("signing a builder image", func() { it("does not populate the signature paths when no secrets were present", func() { - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) require.NoError(t, err) require.NotNil(t, builderRecord) require.Empty(t, builderRecord.SignaturePaths) @@ -941,7 +942,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) + _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) require.Error(t, err) }) @@ -964,7 +965,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { }, } - builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) + builderRecord, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{&fakeSecret}, builderTag) require.NoError(t, err) require.NotNil(t, builderRecord) require.NotEmpty(t, builderRecord.SignaturePaths) diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 711ec7a59..169b4db36 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -41,7 +41,8 @@ type BuilderCreator interface { CreateBuilder( ctx context.Context, builderKeychain authn.Keychain, - keychain authn.Keychain, + stackKeychain authn.Keychain, + lifecycleKeychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, clusterLifecycle *buildapi.ClusterLifecycle, @@ -67,7 +68,7 @@ func NewController( clusterStackInformer buildinformers.ClusterStackInformer, clusterLifecycleInformer buildinformers.ClusterLifecycleInformer, secretFetcher Fetcher, -) (*controller.Impl, func()) { +) *controller.Impl { c := &Reconciler{ Client: opt.Client, BuilderLister: builderInformer.Lister(), @@ -105,12 +106,11 @@ func NewController( c.Tracker.OnChanged, buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterStackKind)), )) - // TODO: how is this tested? - //clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( - // controller.EnsureTypeMeta( - // c.Tracker.OnChanged, - // buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), - //)) + clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( + controller.EnsureTypeMeta( + c.Tracker.OnChanged, + buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), + )) buildpackInformer.Informer().AddEventHandler(controller.HandleAll( controller.EnsureTypeMeta( c.Tracker.OnChanged, @@ -122,9 +122,7 @@ func NewController( buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterBuildpackKind)), )) - return impl, func() { - impl.GlobalResync(builderInformer.Informer()) // TODO: can we remove this part? - } + return impl } type Reconciler struct { @@ -185,7 +183,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui c.Tracker.Track(reconciler.Key{ NamespacedName: types.NamespacedName{ - Name: builder.Spec.Lifecycle.Name, // TODO: check + Name: builder.Spec.Lifecycle.Name, // TODO: confirm this is what we want Namespace: metav1.NamespaceAll, }, GroupKind: schema.GroupKind{ @@ -245,7 +243,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui return buildapi.BuilderRecord{}, errors.Errorf("Error: clusterstack '%s' is not ready", clusterStack.Name) } - clusterLifecycle, err := c.ClusterLifecycleLister.Get(builder.Spec.Lifecycle.Name) // TODO: check + clusterLifecycle, err := c.ClusterLifecycleLister.Get(builder.Spec.Lifecycle.Name) // TODO: confirm this is what we want if err != nil { return buildapi.BuilderRecord{}, err } @@ -269,6 +267,17 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui } } + lifecycleKeychain := builderKeychain + if clusterLifecycle.Spec.ServiceAccountRef != nil { + lifecycleKeychain, err = c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ + ServiceAccount: clusterLifecycle.Spec.ServiceAccountRef.Name, + Namespace: clusterLifecycle.Spec.ServiceAccountRef.Namespace, + }) + if err != nil { + return buildapi.BuilderRecord{}, err + } + } + fetcher := cnb.NewRemoteBuildpackFetcher(c.KeychainFactory, clusterStore, buildpacks, clusterBuildpacks) serviceAccountSecrets, err := c.SecretFetcher.SecretsForServiceAccount(ctx, builder.Spec.ServiceAccount(), builder.Namespace) @@ -285,6 +294,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui ctx, builderKeychain, stackKeychain, + lifecycleKeychain, fetcher, clusterStack, clusterLifecycle, diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index d935c9a73..f9839370f 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -41,6 +41,7 @@ type BuilderCreator interface { ctx context.Context, builderKeychain authn.Keychain, stackKeychain authn.Keychain, + lifecycleKeychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, clusterLifecycle *buildapi.ClusterLifecycle, @@ -65,7 +66,7 @@ func NewController( clusterStackInformer buildinformers.ClusterStackInformer, clusterLifecycleInformer buildinformers.ClusterLifecycleInformer, secretFetcher Fetcher, -) (*controller.Impl, func()) { +) *controller.Impl { c := &Reconciler{ Client: opt.Client, ClusterBuilderLister: clusterBuilderInformer.Lister(), @@ -108,16 +109,13 @@ func NewController( c.Tracker.OnChanged, buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterStackKind)), )) - // TODO: how is this tested? - //clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( - // controller.EnsureTypeMeta( - // c.Tracker.OnChanged, - // buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), - //)) - - return impl, func() { - impl.GlobalResync(clusterBuilderInformer.Informer()) // TODO: can we remove this part? - } + clusterLifecycleInformer.Informer().AddEventHandler(controller.HandleAll( + controller.EnsureTypeMeta( + c.Tracker.OnChanged, + buildapi.SchemeGroupVersion.WithKind(buildapi.ClusterLifecycleKind)), + )) + + return impl } type Reconciler struct { @@ -252,6 +250,17 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu } } + lifecycleKeychain := builderKeychain + if clusterLifecycle.Spec.ServiceAccountRef != nil { + lifecycleKeychain, err = c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ + ServiceAccount: clusterLifecycle.Spec.ServiceAccountRef.Name, + Namespace: clusterLifecycle.Spec.ServiceAccountRef.Namespace, + }) + if err != nil { + return buildapi.BuilderRecord{}, err + } + } + fetcher := cnb.NewRemoteBuildpackFetcher(c.KeychainFactory, clusterStore, nil, clusterBuildpacks) serviceAccountSecrets, err := c.SecretFetcher.SecretsForServiceAccount(ctx, builder.Spec.ServiceAccountRef.Name, builder.Spec.ServiceAccountRef.Namespace) @@ -268,6 +277,7 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu ctx, builderKeychain, stackKeychain, + lifecycleKeychain, fetcher, clusterStack, clusterLifecycle, diff --git a/pkg/reconciler/testhelpers/fake_builder_creator.go b/pkg/reconciler/testhelpers/fake_builder_creator.go index 6d6490f4c..7f35480fb 100644 --- a/pkg/reconciler/testhelpers/fake_builder_creator.go +++ b/pkg/reconciler/testhelpers/fake_builder_creator.go @@ -34,6 +34,7 @@ func (f *FakeBuilderCreator) CreateBuilder( ctx context.Context, builderKeychain authn.Keychain, stackKeychain authn.Keychain, + lifecycleKeychain authn.Keychain, fetcher cnb.RemoteBuildpackFetcher, clusterStack *buildapi.ClusterStack, clusterLifecycle *buildapi.ClusterLifecycle, diff --git a/pkg/slsa/attest.go b/pkg/slsa/attest.go index 5ba30ee4d..f10c62740 100644 --- a/pkg/slsa/attest.go +++ b/pkg/slsa/attest.go @@ -96,7 +96,7 @@ func (a *Attester) AttestBuild(build *buildv1alpha2.Build, buildMetadata *cnb.Bu ID: string(builderId), Version: map[string]string{ "kpack": a.Version, - "lifecycle": "", // TODO: we need to get the lifecycle version from the build metadata + "lifecycle": "1.2.3", // TODO: we need to get the lifecycle version from the build metadata }, BuilderDependencies: builderDeps, }, diff --git a/pkg/slsa/attest_test.go b/pkg/slsa/attest_test.go index f4da89c76..3b78cd13e 100644 --- a/pkg/slsa/attest_test.go +++ b/pkg/slsa/attest_test.go @@ -125,8 +125,7 @@ func testAttester(t *testing.T, when spec.G, it spec.S) { attester := &Attester{ Version: "v0.0.0", - LifecycleProvider: &fakeLifecycleProvider{}, - ImageReader: r, + ImageReader: r, Images: config.Images{ BuildInitImage: "build-init-image", BuildInitWindowsImage: "build-init-windows-image", @@ -299,14 +298,3 @@ func testAttester(t *testing.T, when spec.G, it spec.S) { }) }) } - -type fakeLifecycleProvider struct { -} - -func (l *fakeLifecycleProvider) Metadata() (cnb.LifecycleMetadata, error) { - return cnb.LifecycleMetadata{ - LifecycleInfo: cnb.LifecycleInfo{ - Version: "1.2.3", - }, - }, nil -} From 8f39bc42ad44e505813c474e87da4da79e32ba7f Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 11:14:07 -0400 Subject: [PATCH 25/39] Validate that lifecycle image arch & arch variant match builder Signed-off-by: Natalie Arellano --- pkg/cnb/builder_builder.go | 4 ++++ pkg/cnb/create_builder.go | 28 ++++++++++++++++++++------ pkg/cnb/create_builder_test.go | 2 +- pkg/reconciler/builder/builder_test.go | 2 +- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/pkg/cnb/builder_builder.go b/pkg/cnb/builder_builder.go index f43883191..b125a65f3 100644 --- a/pkg/cnb/builder_builder.go +++ b/pkg/cnb/builder_builder.go @@ -51,6 +51,8 @@ type builderBlder struct { runImage string mixins []string os string + arch string + archVariant string additionalLabels map[string]string } @@ -67,6 +69,8 @@ func (bb *builderBlder) AddStack(baseImage v1.Image, clusterStack *buildapi.Clus } bb.os = file.OS + bb.arch = file.Architecture + bb.archVariant = file.Variant bb.baseImage = baseImage bb.stackId = clusterStack.Status.Id bb.mixins = clusterStack.Status.Mixins diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 84f95a477..2ca45ae44 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -63,7 +63,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( return buildapi.BuilderRecord{}, err } - lifecycleLayer, lifecycleMetadata, err := layerForOS(clusterLifecycle, lifecycleImage, builderBldr.os) + lifecycleLayer, lifecycleMetadata, err := layerForOS(clusterLifecycle, lifecycleImage, builderBldr) if err != nil { return buildapi.BuilderRecord{}, err } @@ -128,7 +128,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( return builder, nil } -func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcrv1.Image, os string) (lifecycleLayer ggcrv1.Layer, lifecycleMetadata LifecycleMetadata, err error) { +func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcrv1.Image, builderBlder *builderBlder) (lifecycleLayer ggcrv1.Layer, lifecycleMetadata LifecycleMetadata, err error) { lifecycleMetadata = LifecycleMetadata{ LifecycleInfo: LifecycleInfo{ Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, @@ -169,12 +169,15 @@ func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcr if err != nil || cfg == nil { return fmt.Errorf("failed to get config file: %w", err) } - if cfg.OS != os { // TODO: check arch + if !platformMatches( + builderBlder.os, builderBlder.arch, builderBlder.archVariant, + cfg.OS, cfg.Architecture, cfg.Variant, + ) { return fmt.Errorf( - "validating lifecycle image %s: expected OS to be %s but got %s", + "validating lifecycle image %s: expected platform to be %s/%s/%s but got %s/%s/%s", clusterLifecycle.Status.ResolvedClusterLifecycle.Id, - os, - cfg.OS, + builderBlder.os, builderBlder.arch, builderBlder.archVariant, + cfg.OS, cfg.Architecture, cfg.Variant, ) } return nil @@ -186,6 +189,19 @@ func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcr return lifecycleLayer, lifecycleMetadata, nil } +func platformMatches(wantOS, wantArch, wantArchVariant string, gotOS, gotArch, gotArchVariant string) bool { + if wantOS != gotOS { + return false + } + if wantArch != "" && gotArch != "" && wantArch != gotArch { + return false + } + if wantArchVariant != "" && gotArchVariant != "" && wantArchVariant != gotArchVariant { + return false + } + return true +} + func toCNBAPISet(from buildapi.APISet) APISet { var to APISet for _, f := range from { diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 09cc90c59..f342dd985 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -705,7 +705,7 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { it("errors with unsupported os", func() { _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) - require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected OS to be %s but got %s", lifecycleImage, os, wrongOS)) + require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected platform to be %s// but got %s//", lifecycleImage, os, wrongOS)) }) }) diff --git a/pkg/reconciler/builder/builder_test.go b/pkg/reconciler/builder/builder_test.go index 48002678c..f30725478 100644 --- a/pkg/reconciler/builder/builder_test.go +++ b/pkg/reconciler/builder/builder_test.go @@ -367,7 +367,7 @@ func testBuilderReconciler(t *testing.T, when spec.G, it spec.S) { }, }, BuilderMetadata: []corev1alpha1.BuildpackMetadata{}, - Stack: corev1alpha1.BuildStack{ // TODO: should we check lifecycle on the expected builder? + Stack: corev1alpha1.BuildStack{ RunImage: "example.com/run-image@sha256:123456", ID: "fake.stack.id", }, From 89000bc2a521eb6404f5639926ee58115484a58a Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 11:35:19 -0400 Subject: [PATCH 26/39] Implement remote lifecycle reader Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 7 +++ pkg/cnb/remote_lifecycle_reader.go | 54 +++++++++++++++++++ .../clusterlifecycle/clusterlifecycle.go | 1 - 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 pkg/cnb/remote_lifecycle_reader.go diff --git a/cmd/controller/main.go b/cmd/controller/main.go index f6f1948db..637a3fa33 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -52,6 +52,7 @@ import ( "github.com/pivotal/kpack/pkg/reconciler/buildpack" "github.com/pivotal/kpack/pkg/reconciler/clusterbuilder" "github.com/pivotal/kpack/pkg/reconciler/clusterbuildpack" + "github.com/pivotal/kpack/pkg/reconciler/clusterlifecycle" "github.com/pivotal/kpack/pkg/reconciler/clusterstack" "github.com/pivotal/kpack/pkg/reconciler/clusterstore" "github.com/pivotal/kpack/pkg/reconciler/image" @@ -195,6 +196,10 @@ func main() { RegistryClient: ®istry.Client{}, } + remoteLifecycleReader := &cnb.RemoteLifecycleReader{ + RegistryClient: ®istry.Client{}, + } + builderCreator := &cnb.RemoteBuilderCreator{ RegistryClient: ®istry.Client{}, KpackVersion: cmd.Identifer, @@ -230,6 +235,7 @@ func main() { clusterBuildpackController := clusterbuildpack.NewController(ctx, options, keychainFactory, clusterBuildpackInformer, remoteStoreReader) clusterStoreController := clusterstore.NewController(ctx, options, keychainFactory, clusterStoreInformer, remoteStoreReader) clusterStackController := clusterstack.NewController(ctx, options, keychainFactory, clusterStackInformer, remoteStackReader) + clusterLifecycleController := clusterlifecycle.NewController(ctx, options, keychainFactory, clusterLifecycleInformer, remoteLifecycleReader) stopChan := make(chan struct{}) informerFactory.Start(stopChan) @@ -254,6 +260,7 @@ func main() { err = runGroup( ctx, run(clusterStackController, routinesPerController), + run(clusterLifecycleController, routinesPerController), run(imageController, routinesPerController), run(buildController, routinesPerController), run(builderController, routinesPerController), diff --git a/pkg/cnb/remote_lifecycle_reader.go b/pkg/cnb/remote_lifecycle_reader.go new file mode 100644 index 000000000..8d2c9cc57 --- /dev/null +++ b/pkg/cnb/remote_lifecycle_reader.go @@ -0,0 +1,54 @@ +package cnb + +import ( + "github.com/google/go-containerregistry/pkg/authn" + + buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + "github.com/pivotal/kpack/pkg/registry/imagehelpers" +) + +const lifecycleMetadataLabel = "io.buildpacks.lifecycle.metadata" + +type RemoteLifecycleReader struct { + RegistryClient RegistryClient +} + +func (r *RemoteLifecycleReader) Read(keychain authn.Keychain, clusterLifecycleSpec buildapi.ClusterLifecycleSpec) (buildapi.ResolvedClusterLifecycle, error) { + lifecycleImg, lifecycleIdentifier, err := r.RegistryClient.Fetch(keychain, clusterLifecycleSpec.Image) + if err != nil { + return buildapi.ResolvedClusterLifecycle{}, err + } + + lifecycleMd := LifecycleMetadata{} + err = imagehelpers.GetLabel(lifecycleImg, lifecycleMetadataLabel, &lifecycleMd) + if err != nil { + return buildapi.ResolvedClusterLifecycle{}, err + } + + return buildapi.ResolvedClusterLifecycle{ + Id: lifecycleIdentifier, + Version: lifecycleMd.LifecycleInfo.Version, + API: buildapi.LifecycleAPI{ + BuildpackVersion: lifecycleMd.API.BuildpackVersion, + PlatformVersion: lifecycleMd.API.PlatformVersion, + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{ + Deprecated: toBuildAPISet(lifecycleMd.APIs.Buildpack.Deprecated), + Supported: toBuildAPISet(lifecycleMd.APIs.Buildpack.Supported), + }, + Platform: buildapi.APIVersions{ + Deprecated: toBuildAPISet(lifecycleMd.APIs.Platform.Deprecated), + Supported: toBuildAPISet(lifecycleMd.APIs.Platform.Supported), + }, + }, + }, nil +} + +func toBuildAPISet(from APISet) buildapi.APISet { + var to buildapi.APISet + for _, f := range from { + to = append(to, f) + } + return to +} diff --git a/pkg/reconciler/clusterlifecycle/clusterlifecycle.go b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go index 0edc2903c..50f876a76 100644 --- a/pkg/reconciler/clusterlifecycle/clusterlifecycle.go +++ b/pkg/reconciler/clusterlifecycle/clusterlifecycle.go @@ -28,7 +28,6 @@ const ( //go:generate counterfeiter . ClusterLifecycleReader type ClusterLifecycleReader interface { - // TODO: add implementation Read(keychain authn.Keychain, clusterLifecycleSpec buildapi.ClusterLifecycleSpec) (buildapi.ResolvedClusterLifecycle, error) } From d001458ffc780f701d8f7eb73cd7a2998bb48b67 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 11:38:22 -0400 Subject: [PATCH 27/39] Delete unneeded things Signed-off-by: Natalie Arellano --- pkg/config/lifecycle_provider.go | 235 --------------------- pkg/config/lifecycle_provider_test.go | 228 -------------------- pkg/reconciler/lifecycle/lifecycle.go | 79 ------- pkg/reconciler/lifecycle/lifecycle_test.go | 100 --------- 4 files changed, 642 deletions(-) delete mode 100644 pkg/config/lifecycle_provider.go delete mode 100644 pkg/config/lifecycle_provider_test.go delete mode 100644 pkg/reconciler/lifecycle/lifecycle.go delete mode 100644 pkg/reconciler/lifecycle/lifecycle_test.go diff --git a/pkg/config/lifecycle_provider.go b/pkg/config/lifecycle_provider.go deleted file mode 100644 index dd04cbd61..000000000 --- a/pkg/config/lifecycle_provider.go +++ /dev/null @@ -1,235 +0,0 @@ -package config - -// TODO: pkg/config/lifecycle_provider.go can probably just be removed? - -import ( - "context" - "sync/atomic" - - "github.com/google/go-containerregistry/pkg/authn" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/pkg/errors" - corev1 "k8s.io/api/core/v1" - "knative.dev/pkg/system" - - "github.com/pivotal/kpack/pkg/cnb" - "github.com/pivotal/kpack/pkg/registry" - "github.com/pivotal/kpack/pkg/registry/imagehelpers" -) - -const ( - LifecycleConfigName = "lifecycle-image" - LifecycleConfigKey = "image" - serviceAccountNameKey = "serviceAccountRef.name" - serviceAccountNamespaceKey = "serviceAccountRef.namespace" - lifecycleMetadataLabel = "io.buildpacks.lifecycle.metadata" -) - -type RegistryClient interface { - Fetch(keychain authn.Keychain, repoName string) (v1.Image, string, error) -} - -type LifecycleProvider struct { - registryClient RegistryClient - keychainFactory registry.KeychainFactory - lifecycleData atomic.Value - handlers []func() -} - -func NewLifecycleProvider(client RegistryClient, keychainFactory registry.KeychainFactory) *LifecycleProvider { - return &LifecycleProvider{ - registryClient: client, - keychainFactory: keychainFactory, - } -} - -func (l *LifecycleProvider) Metadata() (cnb.LifecycleMetadata, error) { - lifecycle, err := l.lifecycle() - if err != nil { - return cnb.LifecycleMetadata{}, err - } - return lifecycle.metadata, nil -} - -func (l *LifecycleProvider) LayerForOS(os string) (v1.Layer, cnb.LifecycleMetadata, error) { - lifecycle, err := l.lifecycle() - if err != nil { - return nil, cnb.LifecycleMetadata{}, err - } - - switch os { - case "linux": - layer, err := lifecycle.linux.toLazyLayer(lifecycle.keychain) - return layer, lifecycle.metadata, err - case "windows": - layer, err := lifecycle.windows.toLazyLayer(lifecycle.keychain) - return layer, lifecycle.metadata, err - default: - return nil, cnb.LifecycleMetadata{}, errors.Errorf("unrecognized os %s", os) - } -} - -func (l *LifecycleProvider) UpdateImage(cm *corev1.ConfigMap) error { - lifecycle, err := l.read(context.Background(), cm) - if err != nil { - l.lifecycleData.Store(configmapRead{err: err}) - return err - } - - if l.isNewImage(lifecycle) { - l.callHandlers() - } - l.lifecycleData.Store(configmapRead{lifecycle: lifecycle}) - return nil -} - -func (l *LifecycleProvider) AddEventHandler(handler func()) { - l.handlers = append(l.handlers, handler) -} - -func (l *LifecycleProvider) read(ctx context.Context, cm *corev1.ConfigMap) (*lifecycle, error) { - imageRef, ok := cm.Data[LifecycleConfigKey] - if !ok { - return nil, errors.Errorf("%s config invalid", LifecycleConfigName) - } - - keychain, err := l.keychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ - ServiceAccount: cm.Data[serviceAccountNameKey], - Namespace: cm.Data[serviceAccountNamespaceKey], - }) - if err != nil { - return nil, errors.Wrapf(err, "fetching keychain to read lifecycle") - } - - img, _, err := l.registryClient.Fetch(keychain, imageRef) - if err != nil { - return nil, errors.Wrap(err, "failed to fetch lifecycle image") - } - - lifecycleMd := cnb.LifecycleMetadata{} - err = imagehelpers.GetLabel(img, lifecycleMetadataLabel, &lifecycleMd) - if err != nil { - return nil, err - } - - digest, err := img.Digest() - if err != nil { - return nil, err - } - - linuxLayer, err := lifecycleLayerForOS(imageRef, img, "linux") - if err != nil { - return nil, err - } - - windowsLayer, err := lifecycleLayerForOS(imageRef, img, "windows") - if err != nil { - return nil, err - } - - return &lifecycle{ - keychain: keychain, - digest: digest, - metadata: lifecycleMd, - linux: linuxLayer, - windows: windowsLayer, - }, nil -} - -func lifecycleLayerForOS(imageRef string, image v1.Image, os string) (*lifecycleLayer, error) { - diffId, err := imagehelpers.GetStringLabel(image, os) - if err != nil { - return nil, errors.Wrapf(err, "could not find lifecycle for os: %s", os) - } - - diffID, err := v1.NewHash(diffId) - if err != nil { - return nil, err - } - - layer, err := image.LayerByDiffID(diffID) - if err != nil { - return nil, err - } - - digest, err := layer.Digest() - if err != nil { - return nil, err - } - - size, err := layer.Size() - if err != nil { - return nil, err - } - - return &lifecycleLayer{ - Digest: digest.String(), - DiffId: diffID.String(), - Image: imageRef, - Size: size, - }, nil -} - -func (l *LifecycleProvider) isLifecycleLoaded() bool { - _, ok := l.lifecycleData.Load().(configmapRead) - return ok -} - -func (l *LifecycleProvider) lifecycle() (*lifecycle, error) { - d, ok := l.lifecycleData.Load().(configmapRead) - if !ok { - return nil, errors.Errorf("Error: lifecycle image has not been loaded from ConfigMap '%s' in namespace '%s'", LifecycleConfigName, system.Namespace()) - } - - return d.lifecycle, d.err -} - -func (l *LifecycleProvider) isNewImage(newLifecycle *lifecycle) bool { - if !l.isLifecycleLoaded() { - return true - } - - lifecycle, err := l.lifecycle() - if err != nil { - return false - } - - return lifecycle.digest.String() != newLifecycle.digest.String() -} - -func (l *LifecycleProvider) callHandlers() { - for _, cb := range l.handlers { - cb() - } -} - -type configmapRead struct { - lifecycle *lifecycle - err error -} - -type lifecycle struct { - digest v1.Hash - metadata cnb.LifecycleMetadata - linux *lifecycleLayer - windows *lifecycleLayer - keychain authn.Keychain -} - -type lifecycleLayer struct { - Digest string - DiffId string - Image string - Size int64 - Keychain authn.Keychain -} - -func (l *lifecycleLayer) toLazyLayer(keychain authn.Keychain) (v1.Layer, error) { - return imagehelpers.NewLazyMountableLayer(imagehelpers.LazyMountableLayerArgs{ - Digest: l.Digest, - DiffId: l.DiffId, - Image: l.Image, - Size: l.Size, - Keychain: keychain, - }) -} diff --git a/pkg/config/lifecycle_provider_test.go b/pkg/config/lifecycle_provider_test.go deleted file mode 100644 index 49a2fd601..000000000 --- a/pkg/config/lifecycle_provider_test.go +++ /dev/null @@ -1,228 +0,0 @@ -package config - -import ( - "testing" - - "github.com/google/go-containerregistry/pkg/authn" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/random" - "github.com/google/go-containerregistry/pkg/v1/types" - "github.com/sclevine/spec" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - - "github.com/pivotal/kpack/pkg/cnb" - "github.com/pivotal/kpack/pkg/registry" - "github.com/pivotal/kpack/pkg/registry/imagehelpers" - "github.com/pivotal/kpack/pkg/registry/registryfakes" -) - -func TestProvider(t *testing.T) { - spec.Run(t, "LifecycleProvider", testProvider) -} - -func testProvider(t *testing.T, when spec.G, it spec.S) { - var ( - lifecycleMetadata = cnb.LifecycleMetadata{ - LifecycleInfo: cnb.LifecycleInfo{ - Version: "0.5.0", - }, - API: cnb.LifecycleAPI{ - BuildpackVersion: "0.2", - PlatformVersion: "0.1", - }, - APIs: cnb.LifecycleAPIs{ - Buildpack: cnb.APIVersions{ - Deprecated: []string{"0.2"}, - Supported: []string{"0.3"}, - }, - Platform: cnb.APIVersions{ - Deprecated: []string{"0.3"}, - Supported: []string{"0.4"}, - }, - }, - } - client = registryfakes.NewFakeClient() - keychain = authn.NewMultiKeychain(authn.DefaultKeychain) - lifecycleImgRef = "some-image" - lifecycleImg v1.Image - linuxLayer v1.Layer - windowsLayer v1.Layer - callBack *fakeCallback - keychainFactory = ®istryfakes.FakeKeychainFactory{} - p *LifecycleProvider - ) - - it.Before(func() { - linuxLayer = testLayer(t) - windowsLayer = testLayer(t) - lifecycleImg = generateLifecycleImage(t, lifecycleMetadata, linuxLayer, windowsLayer) - - keychainFactory.AddKeychainForSecretRef(t, registry.SecretRef{Namespace: "some-service-account-namespace", ServiceAccount: "some-service-account"}, keychain) - client.AddImage(lifecycleImgRef, lifecycleImg, keychain) - client.AddImage("some-other-lifecycle-image", generateLifecycleImage(t, lifecycleMetadata, testLayer(t), testLayer(t)), keychain) - - p = NewLifecycleProvider(client, keychainFactory) - callBack = &fakeCallback{} - p.AddEventHandler(callBack.callBack) - }) - - when("Update Image is called with a ConfigMap", func() { - it("calls all Handlers", func() { - require.Equal(t, callBack.called, 0) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": lifecycleImgRef, "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 1) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": "some-other-lifecycle-image", "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 2) - }) - - it("does not call Handlers when lifecycle image is not updated", func() { - require.Equal(t, callBack.called, 0) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": lifecycleImgRef, "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 1) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": lifecycleImgRef, "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 1) - }) - - it("does not call Handlers when updated lifecycle image is invalid", func() { - require.Equal(t, callBack.called, 0) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": lifecycleImgRef, "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 1) - - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": "some-invalid-image", "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - require.Equal(t, callBack.called, 1) - - _, _, err := p.LayerForOS("linux") - require.Error(t, err) - }) - }) - - when("LayerForOS()", func() { - it.Before(func() { - p.UpdateImage(&corev1.ConfigMap{ - Data: map[string]string{"image": lifecycleImgRef, "serviceAccountRef.name": "some-service-account", "serviceAccountRef.namespace": "some-service-account-namespace"}, - }) - }) - - it("returns the linux layer as a lazy layer", func() { - layer, readMetadata, err := p.LayerForOS("linux") - require.NoError(t, err) - require.Equal(t, readMetadata, lifecycleMetadata) - - expectedDigest, err := linuxLayer.Digest() - require.NoError(t, err) - - expectedDiffID, err := linuxLayer.DiffID() - require.NoError(t, err) - - expectedSize, err := linuxLayer.Size() - require.NoError(t, err) - - expectedLayer, err := imagehelpers.NewLazyMountableLayer(imagehelpers.LazyMountableLayerArgs{ - Digest: expectedDigest.String(), - DiffId: expectedDiffID.String(), - Image: lifecycleImgRef, - Size: expectedSize, - Keychain: keychain, - }) - require.NoError(t, err) - - require.Equal(t, expectedLayer, layer) - - }) - - it("returns the windows layer as a lazy layer", func() { - layer, readMetadata, err := p.LayerForOS("windows") - require.NoError(t, err) - require.Equal(t, readMetadata, lifecycleMetadata) - - expectedDigest, err := windowsLayer.Digest() - require.NoError(t, err) - - expectedDiffID, err := windowsLayer.DiffID() - require.NoError(t, err) - - expectedSize, err := windowsLayer.Size() - require.NoError(t, err) - - expectedLayer, err := imagehelpers.NewLazyMountableLayer(imagehelpers.LazyMountableLayerArgs{ - Digest: expectedDigest.String(), - DiffId: expectedDiffID.String(), - Image: lifecycleImgRef, - Size: expectedSize, - Keychain: keychain, - }) - require.NoError(t, err) - - require.Equal(t, expectedLayer, layer) - }) - - it("returns error on invalid os", func() { - _, _, err := p.LayerForOS("kpack-invalid-test-os") - require.EqualError(t, err, "unrecognized os kpack-invalid-test-os") - }) - - it("can return the metadata by itself", func() { - readMetadata, err := p.Metadata() - require.NoError(t, err) - require.Equal(t, readMetadata, lifecycleMetadata) - }) - }) -} - -type fakeCallback struct { - called int -} - -func (cb *fakeCallback) callBack() { - cb.called++ -} - -func generateLifecycleImage(t *testing.T, metadata cnb.LifecycleMetadata, linuxLifecycle, windowsLifecycle v1.Layer) v1.Image { - lifecycleImg, err := mutate.AppendLayers(empty.Image, linuxLifecycle, windowsLifecycle) - require.NoError(t, err) - - linuxDiffID, err := linuxLifecycle.DiffID() - require.NoError(t, err) - - lifecycleImg, err = imagehelpers.SetStringLabel(lifecycleImg, "linux", linuxDiffID.String()) - require.NoError(t, err) - - windowsDiffId, err := windowsLifecycle.DiffID() - require.NoError(t, err) - - lifecycleImg, err = imagehelpers.SetStringLabel(lifecycleImg, "windows", windowsDiffId.String()) - require.NoError(t, err) - - lifecycleImg, err = imagehelpers.SetLabels(lifecycleImg, map[string]interface{}{ - lifecycleMetadataLabel: metadata, - }) - require.NoError(t, err) - - return lifecycleImg -} - -func testLayer(t *testing.T) v1.Layer { - linuxLifecycle, err := random.Layer(10, types.DockerLayer) - require.NoError(t, err) - return linuxLifecycle -} diff --git a/pkg/reconciler/lifecycle/lifecycle.go b/pkg/reconciler/lifecycle/lifecycle.go deleted file mode 100644 index a9a867dc5..000000000 --- a/pkg/reconciler/lifecycle/lifecycle.go +++ /dev/null @@ -1,79 +0,0 @@ -package lifecycle - -import ( - "context" - "fmt" - "github.com/pivotal/kpack/pkg/tracker" - corev1 "k8s.io/api/core/v1" - - "k8s.io/apimachinery/pkg/types" - coreinformers "k8s.io/client-go/informers/core/v1" - k8sclient "k8s.io/client-go/kubernetes" - corelisters "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - "knative.dev/pkg/controller" - "knative.dev/pkg/logging" - "knative.dev/pkg/system" - - "github.com/pivotal/kpack/pkg/reconciler" -) - -type LifecycleProvider interface { - UpdateImage(cm *corev1.ConfigMap) error -} - -func NewController( - ctx context.Context, - opt reconciler.Options, - k8sClient k8sclient.Interface, - configmapName string, - configMapInformer coreinformers.ConfigMapInformer, - lifecycleProvider LifecycleProvider, -) *controller.Impl { - key := types.NamespacedName{ - Namespace: system.Namespace(), - Name: configmapName, - } - - c := &Reconciler{ - Key: key, - ConfigMapLister: configMapInformer.Lister(), - K8sClient: k8sClient, - LifecycleProvider: lifecycleProvider, - } - - const queueName = "lifecycle" - impl := controller.NewContext(ctx, c, controller.ControllerOptions{WorkQueueName: queueName, Logger: logging.FromContext(ctx).Named(queueName)}) - - // Reconcile when the lifecycle configmap changes. - configMapInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ - FilterFunc: controller.FilterWithNameAndNamespace(key.Namespace, key.Name), - Handler: controller.HandleAll(impl.Enqueue), - }) - - c.Tracker = tracker.New(impl.EnqueueKey, opt.TrackerResyncPeriod()) - - return impl -} - -type Reconciler struct { - Key types.NamespacedName - ConfigMapLister corelisters.ConfigMapLister - Tracker reconciler.Tracker - K8sClient k8sclient.Interface - LifecycleProvider LifecycleProvider -} - -func (c *Reconciler) Reconcile(ctx context.Context, key string) error { - namespace, configMapName, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - return fmt.Errorf("failed splitting meta namespace key: %s", err) - } - - lifecycleConfigMap, err := c.ConfigMapLister.ConfigMaps(namespace).Get(configMapName) - if err != nil { - return err - } - - return c.LifecycleProvider.UpdateImage(lifecycleConfigMap) -} diff --git a/pkg/reconciler/lifecycle/lifecycle_test.go b/pkg/reconciler/lifecycle/lifecycle_test.go deleted file mode 100644 index 24e2e0935..000000000 --- a/pkg/reconciler/lifecycle/lifecycle_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package lifecycle_test - -import ( - "context" - "fmt" - "testing" - - "github.com/sclevine/spec" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - k8sfake "k8s.io/client-go/kubernetes/fake" - "knative.dev/pkg/controller" - - "github.com/pivotal/kpack/pkg/config" - "github.com/pivotal/kpack/pkg/reconciler/lifecycle" - "github.com/pivotal/kpack/pkg/reconciler/testhelpers" -) - -func TestLifecycleReconciler(t *testing.T) { - spec.Run(t, "Lifecycle Reconciler", testLifecycleReconciler) -} - -func testLifecycleReconciler(t *testing.T, when spec.G, it spec.S) { - - var ( - fakeTracker = &testhelpers.FakeTracker{} - lifecycleImageRef = "gcr.io/lifecycle@sha256:some-sha" - serviceAccountName = "lifecycle-sa" - namespace = "kpack" - key = types.NamespacedName{Namespace: namespace, Name: config.LifecycleConfigName} - lifecycleProvider = &fakeLifecycleProvider{} - ) - - lifecycleConfigMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: config.LifecycleConfigName, - Namespace: namespace, - }, - Data: map[string]string{ - config.LifecycleConfigKey: lifecycleImageRef, - "serviceAccountRef.name": serviceAccountName, - "serviceAccountRef.namespace": namespace, - }, - } - listers := testhelpers.NewListers([]runtime.Object{lifecycleConfigMap}) - k8sfakeClient := k8sfake.NewSimpleClientset(listers.GetKubeObjects()...) - - r := &lifecycle.Reconciler{ - Tracker: fakeTracker, - K8sClient: k8sfakeClient, - ConfigMapLister: listers.GetConfigMapLister(), - LifecycleProvider: lifecycleProvider, - } - - when("Reconcile", func() { - it("calls UpdateImage", func() { - err := r.Reconcile(context.TODO(), key.String()) - require.NoError(t, err) - require.Len(t, lifecycleProvider.Calls, 1) - assert.Equal(t, lifecycleProvider.Calls[0], lifecycleConfigMap) - }) - - it("returns error if key is invalid", func() { - err := r.Reconcile(context.TODO(), "my-namespace/fake/config-map") - require.Error(t, err, "unexpected key") - assert.False(t, controller.IsPermanentError(err)) - }) - - it("returns error if config map does not exist", func() { - err := r.Reconcile(context.TODO(), "my-namespace/fake-config-map") - require.Error(t, err, "configmap \"fake-config-map\" not found") - assert.False(t, controller.IsPermanentError(err)) - }) - - it("returns error update image fails", func() { - lifecycleProvider.returnsOnCall(fmt.Errorf("some update error")) - err := r.Reconcile(context.TODO(), "my-namespace/fake-config-map") - require.Error(t, err, "some update error") - assert.False(t, controller.IsPermanentError(err)) - }) - }) -} - -type fakeLifecycleProvider struct { - Calls []*corev1.ConfigMap - error error -} - -func (f *fakeLifecycleProvider) UpdateImage(cm *corev1.ConfigMap) error { - f.Calls = append(f.Calls, cm) - return f.error -} - -func (f *fakeLifecycleProvider) returnsOnCall(err error) { - f.error = err -} From b54d5c56ff9497d4c8612e7c9008eba88e8138ab Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 12:40:54 -0400 Subject: [PATCH 28/39] Fix SLSA Signed-off-by: Natalie Arellano --- pkg/slsa/attest.go | 2 +- pkg/slsa/attest_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/slsa/attest.go b/pkg/slsa/attest.go index f10c62740..adeb58002 100644 --- a/pkg/slsa/attest.go +++ b/pkg/slsa/attest.go @@ -96,7 +96,7 @@ func (a *Attester) AttestBuild(build *buildv1alpha2.Build, buildMetadata *cnb.Bu ID: string(builderId), Version: map[string]string{ "kpack": a.Version, - "lifecycle": "1.2.3", // TODO: we need to get the lifecycle version from the build metadata + "lifecycle": buildMetadata.LifecycleVersion, }, BuilderDependencies: builderDeps, }, diff --git a/pkg/slsa/attest_test.go b/pkg/slsa/attest_test.go index 3b78cd13e..a9cb718a8 100644 --- a/pkg/slsa/attest_test.go +++ b/pkg/slsa/attest_test.go @@ -57,7 +57,8 @@ func testAttester(t *testing.T, when spec.G, it spec.S) { } buildMetadata := &cnb.BuildMetadata{ - LatestImage: "some-registry.io/some/repo@sha256:27227f3eaf20afcd527f31bcaaa1a10d14f30c2a99b313c86b981906c54c07b9", + LatestImage: "some-registry.io/some/repo@sha256:27227f3eaf20afcd527f31bcaaa1a10d14f30c2a99b313c86b981906c54c07b9", + LifecycleVersion: "1.2.3", } pod := &corev1.Pod{ @@ -137,7 +138,7 @@ func testAttester(t *testing.T, when spec.G, it spec.S) { Features: config.FeatureFlags{InjectedSidecarSupport: false}, } - it("", func() { + it("returns the expected, properly indented statement", func() { stmt, err := attester.AttestBuild(build, buildMetadata, pod, authn.DefaultKeychain, UnsignedBuildID) require.NoError(t, err) From 94736b7e6c99cbababf4e8a0795b56b8ea029bdc Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 13:21:41 -0400 Subject: [PATCH 29/39] Trigger a new build when the lifecycle changes Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/builder_resource.go | 2 +- pkg/apis/build/v1alpha2/image_builds.go | 1 - pkg/apis/build/v1alpha2/image_builds_test.go | 23 +- pkg/buildchange/lifecycle_change.go | 52 +++++ pkg/duckbuilder/duck_builder.go | 8 +- pkg/reconciler/image/build_required.go | 15 +- pkg/reconciler/image/build_required_test.go | 51 ++++- pkg/reconciler/image/image_test.go | 226 +++++++++++++++++-- 8 files changed, 338 insertions(+), 40 deletions(-) create mode 100644 pkg/buildchange/lifecycle_change.go diff --git a/pkg/apis/build/v1alpha2/builder_resource.go b/pkg/apis/build/v1alpha2/builder_resource.go index ace3ee071..91ef93022 100644 --- a/pkg/apis/build/v1alpha2/builder_resource.go +++ b/pkg/apis/build/v1alpha2/builder_resource.go @@ -10,7 +10,7 @@ type BuilderResource interface { UpToDate() bool BuildpackMetadata() corev1alpha1.BuildpackMetadataList RunImage() string - // TODO: confirm, should we add LifecycleVersion here? + LifecycleImage() string GetKind() string ConditionReadyMessage() string } diff --git a/pkg/apis/build/v1alpha2/image_builds.go b/pkg/apis/build/v1alpha2/image_builds.go index e48c7a5fd..d45a56ca4 100644 --- a/pkg/apis/build/v1alpha2/image_builds.go +++ b/pkg/apis/build/v1alpha2/image_builds.go @@ -29,7 +29,6 @@ const ( BuildReasonCommit = "COMMIT" BuildReasonBuildpack = "BUILDPACK" BuildReasonStack = "STACK" - // TODO: use this somewhere BuildReasonLifecycle = "LIFECYCLE" BuildReasonTrigger = "TRIGGER" ) diff --git a/pkg/apis/build/v1alpha2/image_builds_test.go b/pkg/apis/build/v1alpha2/image_builds_test.go index d4dd42924..da3865515 100644 --- a/pkg/apis/build/v1alpha2/image_builds_test.go +++ b/pkg/apis/build/v1alpha2/image_builds_test.go @@ -371,15 +371,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - Kind string - LatestImage string - LatestRunImage string - Name string - Namespace string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + Kind string + LatestImage string + LatestRunImage string + LatestLifecycleImage string + Name string + Namespace string } func (t TestBuilderResource) ConditionReadyMessage() string { @@ -409,6 +410,10 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } +func (t TestBuilderResource) LifecycleImage() string { + return t.LatestLifecycleImage +} + func (t TestBuilderResource) GetName() string { return t.Name } diff --git a/pkg/buildchange/lifecycle_change.go b/pkg/buildchange/lifecycle_change.go new file mode 100644 index 000000000..c0f9c2da0 --- /dev/null +++ b/pkg/buildchange/lifecycle_change.go @@ -0,0 +1,52 @@ +package buildchange + +import ( + "strings" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/pkg/errors" + + buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" +) + +func NewLifecycleChange(oldLifecycleImageRefStr, newLifecycleImageRefStr string) Change { + var change lifecycleChange + var errStrs []string + + oldLifecycleImageRef, err := name.ParseReference(oldLifecycleImageRefStr) + if err != nil { + errStrs = append(errStrs, err.Error()) + } else { + change.oldLifecycleImageDigest = oldLifecycleImageRef.Identifier() + } + + newLifecycleImageRef, err := name.ParseReference(newLifecycleImageRefStr) + if err != nil { + errStrs = append(errStrs, err.Error()) + } else { + change.newLifecycleImageDigest = newLifecycleImageRef.Identifier() + } + + if len(errStrs) > 0 { + change.err = errors.New(strings.Join(errStrs, "; ")) + } + return change +} + +type lifecycleChange struct { + oldLifecycleImageDigest string + newLifecycleImageDigest string + err error +} + +func (l lifecycleChange) Reason() buildapi.BuildReason { return buildapi.BuildReasonLifecycle } + +func (l lifecycleChange) IsBuildRequired() (bool, error) { + return l.oldLifecycleImageDigest != l.newLifecycleImageDigest, l.err +} + +func (l lifecycleChange) Old() interface{} { return l.oldLifecycleImageDigest } + +func (l lifecycleChange) New() interface{} { return l.newLifecycleImageDigest } + +func (l lifecycleChange) Priority() buildapi.BuildPriority { return buildapi.BuildPriorityLow } diff --git a/pkg/duckbuilder/duck_builder.go b/pkg/duckbuilder/duck_builder.go index db0c21bc5..4df461afb 100644 --- a/pkg/duckbuilder/duck_builder.go +++ b/pkg/duckbuilder/duck_builder.go @@ -38,8 +38,8 @@ func (b *DuckBuilder) Ready() bool { } func (b *DuckBuilder) UpToDate() bool { - return b.Status.GetCondition(buildapi.ConditionUpToDate).IsTrue() && - (b.Generation == b.Status.ObservedGeneration) + return b.Status.GetCondition(buildapi.ConditionUpToDate).IsTrue() && + (b.Generation == b.Status.ObservedGeneration) } func (b *DuckBuilder) BuildBuilderSpec() corev1alpha1.BuildBuilderSpec { @@ -57,6 +57,10 @@ func (b *DuckBuilder) RunImage() string { return b.Status.Stack.RunImage } +func (b *DuckBuilder) LifecycleImage() string { + return b.Status.Lifecycle.Id +} + func (b *DuckBuilder) ConditionReadyMessage() string { condition := b.Status.GetCondition(corev1alpha1.ConditionReady) if condition == nil { diff --git a/pkg/reconciler/image/build_required.go b/pkg/reconciler/image/build_required.go index a01841164..8fecf502f 100644 --- a/pkg/reconciler/image/build_required.go +++ b/pkg/reconciler/image/build_required.go @@ -33,8 +33,8 @@ func newBuildRequiredResult(summary buildchange.ChangeSummary) buildRequiredResu func isBuildRequired(img *buildapi.Image, lastBuild *buildapi.Build, srcResolver *buildapi.SourceResolver, - builder buildapi.BuilderResource) (buildRequiredResult, error) { - + builder buildapi.BuilderResource, +) (buildRequiredResult, error) { result := buildRequiredResult{ConditionStatus: corev1.ConditionUnknown} if !srcResolver.Ready() || !builder.Ready() { return result, nil @@ -46,6 +46,7 @@ func isBuildRequired(img *buildapi.Image, Process(configChange(img, lastBuild, srcResolver)). Process(buildpackChange(lastBuild, builder)). Process(stackChange(lastBuild, builder)). + Process(lifecycleChange(lastBuild, builder)). Summarize() if err != nil { return result, err @@ -131,3 +132,13 @@ func stackChange(lastBuild *buildapi.Build, builder buildapi.BuilderResource) bu newRunImageRefStr := builder.RunImage() return buildchange.NewStackChange(oldRunImageRefStr, newRunImageRefStr) } + +func lifecycleChange(lastBuild *buildapi.Build, builder buildapi.BuilderResource) buildchange.Change { + if lastBuild == nil || !lastBuild.IsSuccess() { + return nil + } + + oldLifecycleImageRefStr := lastBuild.Status.Lifecycle.Id + newLifecycleImageRefStr := builder.LifecycleImage() + return buildchange.NewLifecycleChange(oldLifecycleImageRefStr, newLifecycleImageRefStr) +} diff --git a/pkg/reconciler/image/build_required_test.go b/pkg/reconciler/image/build_required_test.go index c39444c16..3720badb8 100644 --- a/pkg/reconciler/image/build_required_test.go +++ b/pkg/reconciler/image/build_required_test.go @@ -63,8 +63,9 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { BuilderMetadata: []corev1alpha1.BuildpackMetadata{ {Id: "buildpack.matches", Version: "1"}, }, - LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", - Kind: buildapi.BuilderKind, + LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + LatestLifecycleImage: "some.registry.io/lifecycle-image@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + Kind: buildapi.BuilderKind, } latestBuild := &buildapi.Build{ @@ -93,6 +94,9 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stack.bionic", }, LatestImage: "some.registry.io/built@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some.registry.io/lifecycle-image@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, }, } @@ -397,6 +401,26 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { assert.Equal(t, buildapi.BuildPriorityClassLow, result.PriorityClass) assert.Equal(t, expectedChanges, result.ChangesStr) }) + + it("true if builder has a different lifecycle image", func() { + builder.LatestLifecycleImage = "some.registry.io/lifecycle-image@sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + + expectedChanges := testhelpers.CompactJSON(` +[ + { + "reason": "LIFECYCLE", + "old": "sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + "new": "sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + } +]`) + + result, err := isBuildRequired(image, latestBuild, sourceResolver, builder) + assert.NoError(t, err) + assert.Equal(t, corev1.ConditionTrue, result.ConditionStatus) + assert.Equal(t, buildapi.BuildReasonLifecycle, result.ReasonsStr) + assert.Equal(t, buildapi.BuildPriorityClassLow, result.PriorityClass) + assert.Equal(t, expectedChanges, result.ChangesStr) + }) }) when("Git", func() { @@ -799,15 +823,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - LatestImage string - LatestRunImage string - Name string - Namespace string - Kind string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + LatestImage string + LatestRunImage string + LatestLifecycleImage string + Name string + Namespace string + Kind string } func (t TestBuilderResource) BuildBuilderSpec() corev1alpha1.BuildBuilderSpec { @@ -833,6 +858,10 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } +func (t TestBuilderResource) LifecycleImage() string { + return t.LatestLifecycleImage +} + func (t TestBuilderResource) GetName() string { return t.Name } diff --git a/pkg/reconciler/image/image_test.go b/pkg/reconciler/image/image_test.go index d8b04d7e7..bb1ded839 100644 --- a/pkg/reconciler/image/image_test.go +++ b/pkg/reconciler/image/image_test.go @@ -75,7 +75,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { return r, actionRecorderList, eventList }) -imageWithBuilder := &buildapi.Image{ + imageWithBuilder := &buildapi.Image{ ObjectMeta: metav1.ObjectMeta{ Name: imageName, Namespace: namespace, @@ -166,6 +166,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -787,15 +790,15 @@ imageWithBuilder := &buildapi.Image{ Objects: []runtime.Object{ imageWithBuilder, builderWithCondition( - builder, + builder, corev1alpha1.Condition{ Type: corev1alpha1.ConditionReady, Status: corev1.ConditionFalse, Message: "something went wrong", }, corev1alpha1.Condition{ - Type: buildapi.ConditionUpToDate, - Status: corev1.ConditionFalse, + Type: buildapi.ConditionUpToDate, + Status: corev1.ConditionFalse, }, ), resolvedSourceResolver(imageWithBuilder), @@ -844,16 +847,16 @@ imageWithBuilder := &buildapi.Image{ Objects: []runtime.Object{ imageWithBuilder, builderWithCondition( - builder, + builder, corev1alpha1.Condition{ - Type: corev1alpha1.ConditionReady, - Status: corev1.ConditionTrue, + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, }, corev1alpha1.Condition{ Type: buildapi.ConditionUpToDate, Status: corev1.ConditionFalse, Message: "Builder failed to reconcile", - Reason: buildapi.ReconcileFailedReason, + Reason: buildapi.ReconcileFailedReason, }, ), sourceResolver, @@ -921,10 +924,10 @@ imageWithBuilder := &buildapi.Image{ ObjectMeta: imageWithBuilder.ObjectMeta, Spec: imageWithBuilder.Spec, Status: buildapi.ImageStatus{ - LatestBuildRef: "image-name-build-1", + LatestBuildRef: "image-name-build-1", LatestBuildImageGeneration: 1, - BuildCounter: 1, - LatestBuildReason: "CONFIG", + BuildCounter: 1, + LatestBuildReason: "CONFIG", Status: corev1alpha1.Status{ ObservedGeneration: originalGeneration, Conditions: corev1alpha1.Conditions{ @@ -935,9 +938,9 @@ imageWithBuilder := &buildapi.Image{ Message: "Build 'image-name-build-1' is executing", }, { - Type: buildapi.ConditionBuilderReady, - Status: corev1.ConditionTrue, - Reason: buildapi.BuilderReady, + Type: buildapi.ConditionBuilderReady, + Status: corev1.ConditionTrue, + Reason: buildapi.BuilderReady, }, { Type: buildapi.ConditionBuilderUpToDate, @@ -1464,6 +1467,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1624,6 +1630,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1736,6 +1745,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -1784,6 +1796,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -1910,6 +1925,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: updatedBuilderRunImage, ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -1958,6 +1976,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "gcr.io/test-project/install/run@sha256:42841631725942db48b7ba8b788b97374a2ada34c84ee02ca5e02ef3d4b0dfca", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -2039,6 +2060,177 @@ imageWithBuilder := &buildapi.Image{ }) }) + it("schedules a build when the builder lifecycle is updated", func() { + imageWithBuilder.Status.BuildCounter = 1 + imageWithBuilder.Status.LatestBuildRef = "image-name-build-1" + const updatedBuilderImage = "some/builder@sha256:updated" + + sourceResolver := resolvedSourceResolver(imageWithBuilder) + rt.Test(rtesting.TableRow{ + Key: key, + Objects: []runtime.Object{ + imageWithBuilder, + &buildapi.Builder{ + ObjectMeta: metav1.ObjectMeta{ + Name: builderName, + Namespace: namespace, + }, + TypeMeta: metav1.TypeMeta{ + Kind: buildapi.BuilderKind, + }, + Status: buildapi.BuilderStatus{ + Status: corev1alpha1.Status{ + Conditions: corev1alpha1.Conditions{ + { + Type: corev1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + }, + { + Type: buildapi.ConditionUpToDate, + Status: corev1.ConditionTrue, + }, + }, + }, + LatestImage: updatedBuilderImage, + Stack: corev1alpha1.BuildStack{ + RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + ID: "io.buildpacks.stacks.bionic", + }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c", // new lifecycle digest + }, + BuilderMetadata: corev1alpha1.BuildpackMetadataList{ + { + Id: "io.buildpack", + Version: "version", + }, + }, + }, + }, + sourceResolver, + &buildapi.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: "image-name-build-1", + Namespace: namespace, + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(imageWithBuilder), + }, + Labels: map[string]string{ + buildapi.BuildNumberLabel: "1", + buildapi.ImageLabel: imageName, + }, + }, + Spec: buildapi.BuildSpec{ + Tags: []string{imageWithBuilder.Spec.Tag}, + Builder: corev1alpha1.BuildBuilderSpec{ + Image: updatedBuilderImage, + }, + ServiceAccountName: imageWithBuilder.Spec.ServiceAccountName, + Source: corev1alpha1.SourceConfig{ + Git: &corev1alpha1.Git{ + URL: sourceResolver.Status.Source.Git.URL, + Revision: sourceResolver.Status.Source.Git.Revision, + }, + }, + }, + Status: buildapi.BuildStatus{ + LatestImage: imageWithBuilder.Spec.Tag + "@sha256:just-built", + Status: corev1alpha1.Status{ + Conditions: corev1alpha1.Conditions{ + { + Type: corev1alpha1.ConditionSucceeded, + Status: corev1.ConditionTrue, + }, + }, + }, + Stack: corev1alpha1.BuildStack{ + RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + ID: "io.buildpacks.stacks.bionic", + }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", // old lifecycle digest + }, + BuildMetadata: corev1alpha1.BuildpackMetadataList{ + { + Id: "io.buildpack", + Version: "version", + }, + }, + }, + }, + }, + WantErr: false, + WantCreates: []runtime.Object{ + &buildapi.Build{ + ObjectMeta: metav1.ObjectMeta{ + Name: imageName + "-build-2", + Namespace: namespace, + OwnerReferences: []metav1.OwnerReference{ + *kmeta.NewControllerRef(imageWithBuilder), + }, + Labels: map[string]string{ + buildapi.BuildNumberLabel: "2", + buildapi.ImageLabel: imageName, + buildapi.ImageGenerationLabel: generation(imageWithBuilder), + someLabelKey: someValueToPassThrough, + }, + Annotations: map[string]string{ + buildapi.BuilderNameAnnotation: builderName, + buildapi.BuilderKindAnnotation: buildapi.BuilderKind, + buildapi.BuildReasonAnnotation: buildapi.BuildReasonLifecycle, + buildapi.BuildChangesAnnotation: testhelpers.CompactJSON(` +[ + { + "reason": "LIFECYCLE", + "old": "sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + "new": "sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + } +]`), + }, + }, + Spec: buildapi.BuildSpec{ + Tags: []string{imageWithBuilder.Spec.Tag}, + Builder: corev1alpha1.BuildBuilderSpec{ + Image: updatedBuilderImage, + }, + ServiceAccountName: imageWithBuilder.Spec.ServiceAccountName, + Source: corev1alpha1.SourceConfig{ + Git: &corev1alpha1.Git{ + URL: sourceResolver.Status.Source.Git.URL, + Revision: sourceResolver.Status.Source.Git.Revision, + }, + }, + Cache: &buildapi.BuildCacheConfig{}, + RunImage: builderRunImage, + LastBuild: &buildapi.LastBuild{ + Image: "some/image@sha256:just-built", + StackId: "io.buildpacks.stacks.bionic", + }, + }, + }, + }, + WantStatusUpdates: []clientgotesting.UpdateActionImpl{ + { + Object: &buildapi.Image{ + ObjectMeta: imageWithBuilder.ObjectMeta, + Spec: imageWithBuilder.Spec, + Status: buildapi.ImageStatus{ + Status: corev1alpha1.Status{ + ObservedGeneration: originalGeneration, + Conditions: conditionBuildExecuting("image-name-build-2"), + }, + LatestBuildRef: "image-name-build-2", + LatestBuildReason: "LIFECYCLE", + LatestBuildImageGeneration: originalGeneration, + LatestImage: imageWithBuilder.Spec.Tag + "@sha256:just-built", + BuildCounter: 2, + }, + }, + }, + }, + }) + }) + it("schedules a build with previous build's LastBuild if the last build failed", func() { imageWithBuilder.Status.BuildCounter = 2 imageWithBuilder.Status.LatestBuildRef = "image-name-build200001" @@ -2317,6 +2509,9 @@ imageWithBuilder := &buildapi.Image{ RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -2747,6 +2942,9 @@ func builds(image *buildapi.Image, sourceResolver *buildapi.SourceResolver, coun RunImage: runImageRef, ID: "io.buildpacks.stacks.bionic", }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ + Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ condition, From cd19fbb164bf0f8c9d28e7b554ad2c05faa15e64 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Wed, 29 May 2024 16:20:55 -0400 Subject: [PATCH 30/39] Fix controller compile Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 637a3fa33..e253883e2 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -3,7 +3,6 @@ package main import ( "context" "flag" - "fmt" "log" "net/http" "os" @@ -14,7 +13,6 @@ import ( ociremote "github.com/sigstore/cosign/v2/pkg/oci/remote" "go.uber.org/zap" "golang.org/x/sync/errgroup" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/dynamic" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" @@ -153,9 +151,6 @@ func main() { k8sClient, options.ResyncPeriod, informers.WithNamespace(system.Namespace()), - informers.WithTweakListOptions(func(options *metav1.ListOptions) { - options.FieldSelector = fmt.Sprintf("metadata.namespace=%s,metadata.name=%s", system.Namespace(), config.LifecycleConfigName) - }), ) lifecycleConfigmapInformer := lifecycleConfigmapInformerFactory.Core().V1().ConfigMaps() From 20857729d6c3d6eef3746279938a04d4851c92fb Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 4 Jun 2024 13:36:38 -0400 Subject: [PATCH 31/39] Trigger updates on a change to the lifecycle commit, not the lifecycle image digest We don't have the lifecycle image digest when inspecting the build status Signed-off-by: Natalie Arellano --- api/openapi-spec/swagger.json | 23 ++++++---- pkg/apis/build/v1alpha2/build_types.go | 3 +- pkg/apis/build/v1alpha2/builder_resource.go | 2 +- .../build/v1alpha2/cluster_lifecycle_types.go | 2 +- pkg/apis/build/v1alpha2/image_builds_test.go | 24 +++++------ .../build/v1alpha2/zz_generated.deepcopy.go | 1 - pkg/buildchange/lifecycle_change.go | 42 +++++-------------- pkg/cnb/build_metadata.go | 4 +- pkg/cnb/create_builder.go | 10 ++++- pkg/cnb/create_builder_test.go | 11 ++--- pkg/cnb/remote_lifecycle_reader.go | 3 +- pkg/duckbuilder/duck_builder.go | 4 +- pkg/openapi/openapi_generated.go | 26 ++++++++---- pkg/reconciler/build/build.go | 3 +- pkg/reconciler/image/build_required.go | 6 +-- pkg/reconciler/image/build_required_test.go | 42 +++++++++---------- pkg/reconciler/image/image_test.go | 40 ++++++------------ 17 files changed, 111 insertions(+), 135 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index c4fb658d0..b47545bd5 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -5704,13 +5704,10 @@ "kpack.build.v1alpha2.BuildStatus": { "type": "object", "required": [ - "Lifecycle" + "lifecycleVersion", + "lifecycleCommit" ], "properties": { - "Lifecycle": { - "default": {}, - "$ref": "#/definitions/kpack.build.v1alpha2.ResolvedClusterLifecycle" - }, "buildMetadata": { "type": "array", "items": { @@ -5737,6 +5734,14 @@ "latestImage": { "type": "string" }, + "lifecycleCommit": { + "type": "string", + "default": "" + }, + "lifecycleVersion": { + "type": "string", + "default": "" + }, "observedGeneration": { "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", "type": "integer", @@ -6352,6 +6357,9 @@ "default": {}, "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPIs" }, + "commit": { + "type": "string" + }, "conditions": { "description": "Conditions the latest available observations of a resource's current state.", "type": "array", @@ -6362,9 +6370,6 @@ "x-kubernetes-patch-merge-key": "type", "x-kubernetes-patch-strategy": "merge" }, - "id": { - "type": "string" - }, "observedGeneration": { "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", "type": "integer", @@ -7005,7 +7010,7 @@ "default": {}, "$ref": "#/definitions/kpack.build.v1alpha2.LifecycleAPIs" }, - "id": { + "commit": { "type": "string" }, "version": { diff --git a/pkg/apis/build/v1alpha2/build_types.go b/pkg/apis/build/v1alpha2/build_types.go index 0b231d71e..651577c3a 100644 --- a/pkg/apis/build/v1alpha2/build_types.go +++ b/pkg/apis/build/v1alpha2/build_types.go @@ -132,7 +132,8 @@ type BuildStatus struct { corev1alpha1.Status `json:",inline"` BuildMetadata corev1alpha1.BuildpackMetadataList `json:"buildMetadata,omitempty"` Stack corev1alpha1.BuildStack `json:"stack,omitempty"` - Lifecycle ResolvedClusterLifecycle // TODO: confirm + LifecycleVersion string `json:"lifecycleVersion"` + LifecycleCommit string `json:"lifecycleCommit"` LatestImage string `json:"latestImage,omitempty"` LatestCacheImage string `json:"latestCacheImage,omitempty"` LatestAttestationImage string `json:"latestAttestationImage,omitempty"` diff --git a/pkg/apis/build/v1alpha2/builder_resource.go b/pkg/apis/build/v1alpha2/builder_resource.go index 91ef93022..22621b9ca 100644 --- a/pkg/apis/build/v1alpha2/builder_resource.go +++ b/pkg/apis/build/v1alpha2/builder_resource.go @@ -10,7 +10,7 @@ type BuilderResource interface { UpToDate() bool BuildpackMetadata() corev1alpha1.BuildpackMetadataList RunImage() string - LifecycleImage() string + LifecycleCommit() string GetKind() string ConditionReadyMessage() string } diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index a554e15e3..9242ddce2 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -41,8 +41,8 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { - Id string `json:"id,omitempty"` // TODO: confirm, should this be LatestImage? Version string `json:"version,omitempty"` + Commit string `json:"commit,omitempty"` // Deprecated: Use `LifecycleAPIs` instead API LifecycleAPI `json:"api,omitempty"` diff --git a/pkg/apis/build/v1alpha2/image_builds_test.go b/pkg/apis/build/v1alpha2/image_builds_test.go index da3865515..f68f3e133 100644 --- a/pkg/apis/build/v1alpha2/image_builds_test.go +++ b/pkg/apis/build/v1alpha2/image_builds_test.go @@ -371,16 +371,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - Kind string - LatestImage string - LatestRunImage string - LatestLifecycleImage string - Name string - Namespace string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + Kind string + LatestImage string + LatestRunImage string + LatestLifecycleCommit string + Name string + Namespace string } func (t TestBuilderResource) ConditionReadyMessage() string { @@ -410,8 +410,8 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } -func (t TestBuilderResource) LifecycleImage() string { - return t.LatestLifecycleImage +func (t TestBuilderResource) LifecycleCommit() string { + return t.LatestLifecycleCommit } func (t TestBuilderResource) GetName() string { diff --git a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go index e4f4725a5..d5bccc3ab 100644 --- a/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/build/v1alpha2/zz_generated.deepcopy.go @@ -365,7 +365,6 @@ func (in *BuildStatus) DeepCopyInto(out *BuildStatus) { copy(*out, *in) } out.Stack = in.Stack - in.Lifecycle.DeepCopyInto(&out.Lifecycle) if in.StepStates != nil { in, out := &in.StepStates, &out.StepStates *out = make([]v1.ContainerState, len(*in)) diff --git a/pkg/buildchange/lifecycle_change.go b/pkg/buildchange/lifecycle_change.go index c0f9c2da0..bebb6cd0f 100644 --- a/pkg/buildchange/lifecycle_change.go +++ b/pkg/buildchange/lifecycle_change.go @@ -1,52 +1,30 @@ package buildchange import ( - "strings" - - "github.com/google/go-containerregistry/pkg/name" - "github.com/pkg/errors" - buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" ) -func NewLifecycleChange(oldLifecycleImageRefStr, newLifecycleImageRefStr string) Change { - var change lifecycleChange - var errStrs []string - - oldLifecycleImageRef, err := name.ParseReference(oldLifecycleImageRefStr) - if err != nil { - errStrs = append(errStrs, err.Error()) - } else { - change.oldLifecycleImageDigest = oldLifecycleImageRef.Identifier() - } - - newLifecycleImageRef, err := name.ParseReference(newLifecycleImageRefStr) - if err != nil { - errStrs = append(errStrs, err.Error()) - } else { - change.newLifecycleImageDigest = newLifecycleImageRef.Identifier() - } - - if len(errStrs) > 0 { - change.err = errors.New(strings.Join(errStrs, "; ")) +func NewLifecycleChange(oldLifecycle, newLifecycle string) Change { + return lifecycleChange{ + oldLifecycle: oldLifecycle, + newLifecycle: newLifecycle, } - return change } type lifecycleChange struct { - oldLifecycleImageDigest string - newLifecycleImageDigest string - err error + oldLifecycle string + newLifecycle string + err error } func (l lifecycleChange) Reason() buildapi.BuildReason { return buildapi.BuildReasonLifecycle } func (l lifecycleChange) IsBuildRequired() (bool, error) { - return l.oldLifecycleImageDigest != l.newLifecycleImageDigest, l.err + return l.oldLifecycle != l.newLifecycle, l.err } -func (l lifecycleChange) Old() interface{} { return l.oldLifecycleImageDigest } +func (l lifecycleChange) Old() interface{} { return l.oldLifecycle } -func (l lifecycleChange) New() interface{} { return l.newLifecycleImageDigest } +func (l lifecycleChange) New() interface{} { return l.newLifecycle } func (l lifecycleChange) Priority() buildapi.BuildPriority { return buildapi.BuildPriorityLow } diff --git a/pkg/cnb/build_metadata.go b/pkg/cnb/build_metadata.go index 3e0f35496..a2d9b0927 100644 --- a/pkg/cnb/build_metadata.go +++ b/pkg/cnb/build_metadata.go @@ -25,8 +25,8 @@ type BuildMetadata struct { LatestImage string `json:"latestImage"` StackID string `json:"stackID"` StackRunImage string `json:"stackRunImage"` - // TODO: confirm, lifecycle git commit is also available - do we want it? - LifecycleVersion string `json:"lifecycleVersion"` + LifecycleVersion string `json:"lifecycleVersion"` + LifecycleCommit string `json:"lifecycleCommit"` } type ImageFetcher interface { diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 2ca45ae44..6e6b37a42 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -45,7 +45,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( if err != nil { return buildapi.BuilderRecord{}, err } - lifecycleImage, _, err := r.RegistryClient.Fetch(lifecycleKeychain, clusterLifecycle.Status.ResolvedClusterLifecycle.Id) + lifecycleImage, _, err := r.RegistryClient.Fetch(lifecycleKeychain, clusterLifecycle.Spec.Image) // TODO: should there be a "latest image" on the status? if err != nil { return buildapi.BuilderRecord{}, err } @@ -117,6 +117,12 @@ func (r *RemoteBuilderCreator) CreateBuilder( RunImage: relocatedRunImage, ID: clusterStack.Status.Id, }, + Lifecycle: buildapi.ResolvedClusterLifecycle{ // TODO: test + Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, + Commit: clusterLifecycle.Status.ResolvedClusterLifecycle.Commit, + API: clusterLifecycle.Status.ResolvedClusterLifecycle.API, + APIs: clusterLifecycle.Status.ResolvedClusterLifecycle.APIs, + }, Buildpacks: buildpackMetadata(builderBldr.buildpacks()), Order: builderBldr.order, ObservedStackGeneration: clusterStack.Status.ObservedGeneration, @@ -175,7 +181,7 @@ func layerForOS(clusterLifecycle *buildapi.ClusterLifecycle, lifecycleImage ggcr ) { return fmt.Errorf( "validating lifecycle image %s: expected platform to be %s/%s/%s but got %s/%s/%s", - clusterLifecycle.Status.ResolvedClusterLifecycle.Id, + clusterLifecycle.Spec.Image, builderBlder.os, builderBlder.arch, builderBlder.archVariant, cfg.OS, cfg.Architecture, cfg.Variant, ) diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index f342dd985..3efbdf002 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -45,7 +45,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { relocatedRunImageTag = "custom/example:test-builder-run-image" buildImageTag = "paketo-buildpacks/build:full-cnb" runImageTag = "paketo-buildpacks/run:full-cnb" - lifecycleImage = "index.docker.io/buildpacksio/lifecycle@sha256:6dd262f754439ce98c901214934e587fd8a00be8b2b1cdf0b62cdade3d93379b" lifecycleImageTag = "buildpacksio/lifecycle:latest" buildImageLayers = 10 lifecycleImageLayers = 1 @@ -345,12 +344,11 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { lConfig.OS = os lifecycleImg, err = mutate.ConfigFile(lifecycleImg, config) - registryClient.AddImage(lifecycleImage, lifecycleImg, lifecycleKeychain) + registryClient.AddImage(lifecycleImageTag, lifecycleImg, lifecycleKeychain) // cluster lifecycle clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", @@ -700,12 +698,12 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { cfg.OS = wrongOS lifecycleImg, err = mutate.ConfigFile(lifecycleImg, cfg) require.NoError(t, err) - registryClient.AddImage(lifecycleImage, lifecycleImg, lifecycleKeychain) + registryClient.AddImage(lifecycleImageTag, lifecycleImg, lifecycleKeychain) }) it("errors with unsupported os", func() { _, err := subject.CreateBuilder(ctx, builderKeychain, stackKeychain, lifecycleKeychain, fetcher, stack, clusterLifecycle, clusterBuilderSpec, []*corev1.Secret{}, builderTag) - require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected platform to be %s// but got %s//", lifecycleImage, os, wrongOS)) + require.EqualError(t, err, fmt.Sprintf("validating lifecycle image %s: expected platform to be %s// but got %s//", lifecycleImageTag, os, wrongOS)) }) }) @@ -761,7 +759,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { it("works with relaxed mixin contract", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", @@ -853,7 +850,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { it("supports anystack buildpacks", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", @@ -897,7 +893,6 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { when("validating platform api", func() { it("errors if no lifecycle platform api is supported", func() { clusterLifecycle.Status.ResolvedClusterLifecycle = buildapi.ResolvedClusterLifecycle{ - Id: lifecycleImage, Version: "0.5.0", API: buildapi.LifecycleAPI{ BuildpackVersion: "0.2", diff --git a/pkg/cnb/remote_lifecycle_reader.go b/pkg/cnb/remote_lifecycle_reader.go index 8d2c9cc57..edb1dca4f 100644 --- a/pkg/cnb/remote_lifecycle_reader.go +++ b/pkg/cnb/remote_lifecycle_reader.go @@ -14,7 +14,7 @@ type RemoteLifecycleReader struct { } func (r *RemoteLifecycleReader) Read(keychain authn.Keychain, clusterLifecycleSpec buildapi.ClusterLifecycleSpec) (buildapi.ResolvedClusterLifecycle, error) { - lifecycleImg, lifecycleIdentifier, err := r.RegistryClient.Fetch(keychain, clusterLifecycleSpec.Image) + lifecycleImg, _, err := r.RegistryClient.Fetch(keychain, clusterLifecycleSpec.Image) if err != nil { return buildapi.ResolvedClusterLifecycle{}, err } @@ -26,7 +26,6 @@ func (r *RemoteLifecycleReader) Read(keychain authn.Keychain, clusterLifecycleSp } return buildapi.ResolvedClusterLifecycle{ - Id: lifecycleIdentifier, Version: lifecycleMd.LifecycleInfo.Version, API: buildapi.LifecycleAPI{ BuildpackVersion: lifecycleMd.API.BuildpackVersion, diff --git a/pkg/duckbuilder/duck_builder.go b/pkg/duckbuilder/duck_builder.go index 4df461afb..b5924f5a4 100644 --- a/pkg/duckbuilder/duck_builder.go +++ b/pkg/duckbuilder/duck_builder.go @@ -57,8 +57,8 @@ func (b *DuckBuilder) RunImage() string { return b.Status.Stack.RunImage } -func (b *DuckBuilder) LifecycleImage() string { - return b.Status.Lifecycle.Id +func (b *DuckBuilder) LifecycleCommit() string { + return b.Status.Lifecycle.Commit } func (b *DuckBuilder) ConditionReadyMessage() string { diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index be77f4224..d8d512da8 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -2389,10 +2389,18 @@ func schema_pkg_apis_build_v1alpha2_BuildStatus(ref common.ReferenceCallback) co Ref: ref("github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack"), }, }, - "Lifecycle": { + "lifecycleVersion": { SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle"), + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "lifecycleCommit": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", }, }, "latestImage": { @@ -2457,11 +2465,11 @@ func schema_pkg_apis_build_v1alpha2_BuildStatus(ref common.ReferenceCallback) co }, }, }, - Required: []string{"Lifecycle"}, + Required: []string{"lifecycleVersion", "lifecycleCommit"}, }, }, Dependencies: []string{ - "github.com/pivotal/kpack/pkg/apis/build/v1alpha2.ResolvedClusterLifecycle", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "k8s.io/api/core/v1.ContainerState"}, + "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildStack", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.BuildpackMetadata", "github.com/pivotal/kpack/pkg/apis/core/v1alpha1.Condition", "k8s.io/api/core/v1.ContainerState"}, } } @@ -3548,13 +3556,13 @@ func schema_pkg_apis_build_v1alpha2_ClusterLifecycleStatus(ref common.ReferenceC }, }, }, - "id": { + "version": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", }, }, - "version": { + "commit": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", @@ -4766,13 +4774,13 @@ func schema_pkg_apis_build_v1alpha2_ResolvedClusterLifecycle(ref common.Referenc SchemaProps: spec.SchemaProps{ Type: []string{"object"}, Properties: map[string]spec.Schema{ - "id": { + "version": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", }, }, - "version": { + "commit": { SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", diff --git a/pkg/reconciler/build/build.go b/pkg/reconciler/build/build.go index 9ee67e65f..921da3d3b 100644 --- a/pkg/reconciler/build/build.go +++ b/pkg/reconciler/build/build.go @@ -215,7 +215,8 @@ func (c *Reconciler) reconcile(ctx context.Context, build *buildapi.Build) error build.Status.LatestAttestationImage = attestDigest build.Status.Stack.RunImage = buildMetadata.StackRunImage build.Status.Stack.ID = buildMetadata.StackID - build.Status.Lifecycle.Version = buildMetadata.LifecycleVersion + build.Status.LifecycleVersion = buildMetadata.LifecycleVersion + build.Status.LifecycleCommit = buildMetadata.LifecycleCommit } build.Status.PodName = pod.Name diff --git a/pkg/reconciler/image/build_required.go b/pkg/reconciler/image/build_required.go index 8fecf502f..db2c22e28 100644 --- a/pkg/reconciler/image/build_required.go +++ b/pkg/reconciler/image/build_required.go @@ -138,7 +138,7 @@ func lifecycleChange(lastBuild *buildapi.Build, builder buildapi.BuilderResource return nil } - oldLifecycleImageRefStr := lastBuild.Status.Lifecycle.Id - newLifecycleImageRefStr := builder.LifecycleImage() - return buildchange.NewLifecycleChange(oldLifecycleImageRefStr, newLifecycleImageRefStr) + oldLifecycle := lastBuild.Status.LifecycleCommit + newLifecycle := builder.LifecycleCommit() + return buildchange.NewLifecycleChange(oldLifecycle, newLifecycle) } diff --git a/pkg/reconciler/image/build_required_test.go b/pkg/reconciler/image/build_required_test.go index 3720badb8..e5026b3d8 100644 --- a/pkg/reconciler/image/build_required_test.go +++ b/pkg/reconciler/image/build_required_test.go @@ -63,9 +63,9 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { BuilderMetadata: []corev1alpha1.BuildpackMetadata{ {Id: "buildpack.matches", Version: "1"}, }, - LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", - LatestLifecycleImage: "some.registry.io/lifecycle-image@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - Kind: buildapi.BuilderKind, + LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + LatestLifecycleCommit: "some-git-commit", + Kind: buildapi.BuilderKind, } latestBuild := &buildapi.Build{ @@ -93,10 +93,8 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { RunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stack.bionic", }, - LatestImage: "some.registry.io/built@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some.registry.io/lifecycle-image@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LatestImage: "some.registry.io/built@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + LifecycleCommit: "some-git-commit", }, } @@ -403,14 +401,14 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { }) it("true if builder has a different lifecycle image", func() { - builder.LatestLifecycleImage = "some.registry.io/lifecycle-image@sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + builder.LatestLifecycleCommit = "some-new-git-commit" expectedChanges := testhelpers.CompactJSON(` [ { "reason": "LIFECYCLE", - "old": "sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - "new": "sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + "old": "some-git-commit", + "new": "some-new-git-commit" } ]`) @@ -823,16 +821,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - LatestImage string - LatestRunImage string - LatestLifecycleImage string - Name string - Namespace string - Kind string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + LatestImage string + LatestRunImage string + LatestLifecycleCommit string + Name string + Namespace string + Kind string } func (t TestBuilderResource) BuildBuilderSpec() corev1alpha1.BuildBuilderSpec { @@ -858,8 +856,8 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } -func (t TestBuilderResource) LifecycleImage() string { - return t.LatestLifecycleImage +func (t TestBuilderResource) LifecycleCommit() string { + return t.LatestLifecycleCommit } func (t TestBuilderResource) GetName() string { diff --git a/pkg/reconciler/image/image_test.go b/pkg/reconciler/image/image_test.go index bb1ded839..f3389be5e 100644 --- a/pkg/reconciler/image/image_test.go +++ b/pkg/reconciler/image/image_test.go @@ -167,7 +167,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + Commit: "some-commit", }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ @@ -1467,9 +1467,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1630,9 +1628,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1746,7 +1742,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + Commit: "some-commit", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -1796,9 +1792,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -1926,7 +1920,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", + Commit: "some-commit", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -1976,9 +1970,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "gcr.io/test-project/install/run@sha256:42841631725942db48b7ba8b788b97374a2ada34c84ee02ca5e02ef3d4b0dfca", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -2097,7 +2089,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c", // new lifecycle digest + Commit: "some-new-commit", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -2147,9 +2139,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", // old lifecycle digest - }, + LifecycleCommit: "some-commit", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -2182,8 +2172,8 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { [ { "reason": "LIFECYCLE", - "old": "sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - "new": "sha256:7aa7a5359173d05b63cfd682e3c38487f3cb4f7f1d60659fe59fab1505977d4c" + "old": "some-commit", + "new": "some-new-commit" } ]`), }, @@ -2509,9 +2499,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -2942,9 +2930,7 @@ func builds(image *buildapi.Image, sourceResolver *buildapi.SourceResolver, coun RunImage: runImageRef, ID: "io.buildpacks.stacks.bionic", }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ - Id: "some/lifecycle@sha256:01d09d19c2139a46aebfb577780d123d7396e97201bc7ead210a2ebff8239dee", - }, + LifecycleCommit: "some-commit", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ condition, From 905ea8e1dbaa46ddf8c41ce3aaf05b469f926ae0 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 6 Jun 2024 17:10:50 -0400 Subject: [PATCH 32/39] Don't wait for the lifecycle config map informer to be ready, it will never be ready Signed-off-by: Natalie Arellano --- cmd/controller/main.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index e253883e2..a93aa088d 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -28,7 +28,6 @@ import ( "knative.dev/pkg/metrics" "knative.dev/pkg/profiling" "knative.dev/pkg/signals" - "knative.dev/pkg/system" "github.com/pivotal/kpack/cmd" _ "github.com/pivotal/kpack/internal/logrus/fatal" @@ -147,12 +146,6 @@ func main() { if err != nil { log.Fatalf("could not create k8s keychain factory: %s", err) } - lifecycleConfigmapInformerFactory := informers.NewSharedInformerFactoryWithOptions( - k8sClient, - options.ResyncPeriod, - informers.WithNamespace(system.Namespace()), - ) - lifecycleConfigmapInformer := lifecycleConfigmapInformerFactory.Core().V1().ConfigMaps() metadataRetriever := &cnb.RemoteMetadataRetriever{ ImageFetcher: ®istry.Client{}, @@ -235,7 +228,6 @@ func main() { stopChan := make(chan struct{}) informerFactory.Start(stopChan) k8sInformerFactory.Start(stopChan) - lifecycleConfigmapInformerFactory.Start(stopChan) waitForSync(stopChan, buildInformer.Informer(), @@ -243,7 +235,6 @@ func main() { sourceResolverInformer.Informer(), pvcInformer.Informer(), podInformer.Informer(), - lifecycleConfigmapInformer.Informer(), builderInformer.Informer(), buildpackInformer.Informer(), clusterBuilderInformer.Informer(), From 53713229003160e27c89c624a6cf00d8b3bae3c3 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 6 Jun 2024 17:11:20 -0400 Subject: [PATCH 33/39] Emit a better error when the lifecycle isn't ready Signed-off-by: Natalie Arellano --- pkg/reconciler/builder/builder.go | 4 ++++ pkg/reconciler/clusterbuilder/clusterbuilder.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pkg/reconciler/builder/builder.go b/pkg/reconciler/builder/builder.go index 169b4db36..115a016ac 100644 --- a/pkg/reconciler/builder/builder.go +++ b/pkg/reconciler/builder/builder.go @@ -248,6 +248,10 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Bui return buildapi.BuilderRecord{}, err } + if !clusterLifecycle.Status.GetCondition(corev1alpha1.ConditionReady).IsTrue() { + return buildapi.BuilderRecord{}, errors.Errorf("Error: clusterlifecycle '%s' is not ready", clusterLifecycle.Name) + } + builderKeychain, err := c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ ServiceAccount: builder.Spec.ServiceAccount(), Namespace: builder.Namespace, diff --git a/pkg/reconciler/clusterbuilder/clusterbuilder.go b/pkg/reconciler/clusterbuilder/clusterbuilder.go index f9839370f..a7e52c6f2 100644 --- a/pkg/reconciler/clusterbuilder/clusterbuilder.go +++ b/pkg/reconciler/clusterbuilder/clusterbuilder.go @@ -231,6 +231,10 @@ func (c *Reconciler) reconcileBuilder(ctx context.Context, builder *buildapi.Clu return buildapi.BuilderRecord{}, err } + if !clusterLifecycle.Status.GetCondition(corev1alpha1.ConditionReady).IsTrue() { + return buildapi.BuilderRecord{}, errors.Errorf("Error: clusterlifecycle '%s' is not ready", clusterLifecycle.Name) + } + builderKeychain, err := c.KeychainFactory.KeychainForSecretRef(ctx, registry.SecretRef{ ServiceAccount: builder.Spec.ServiceAccountRef.Name, Namespace: builder.Spec.ServiceAccountRef.Namespace, From c1595ab2580e53b9ea5fe0c3b84bd9b2cd1d9364 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 6 Jun 2024 17:34:55 -0400 Subject: [PATCH 34/39] Fix reading lifecycle metadata from image labels Signed-off-by: Natalie Arellano --- pkg/cnb/remote_lifecycle_reader.go | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/pkg/cnb/remote_lifecycle_reader.go b/pkg/cnb/remote_lifecycle_reader.go index edb1dca4f..14d1d5179 100644 --- a/pkg/cnb/remote_lifecycle_reader.go +++ b/pkg/cnb/remote_lifecycle_reader.go @@ -7,7 +7,7 @@ import ( "github.com/pivotal/kpack/pkg/registry/imagehelpers" ) -const lifecycleMetadataLabel = "io.buildpacks.lifecycle.metadata" +const lifecycleBuilderMetadataLabel = "io.buildpacks.builder.metadata" type RemoteLifecycleReader struct { RegistryClient RegistryClient @@ -19,26 +19,32 @@ func (r *RemoteLifecycleReader) Read(keychain authn.Keychain, clusterLifecycleSp return buildapi.ResolvedClusterLifecycle{}, err } - lifecycleMd := LifecycleMetadata{} - err = imagehelpers.GetLabel(lifecycleImg, lifecycleMetadataLabel, &lifecycleMd) + deprecatedLifecycleMD := LifecycleMetadata{} + err = imagehelpers.GetLabel(lifecycleImg, lifecycleBuilderMetadataLabel, &deprecatedLifecycleMD) + if err != nil { + return buildapi.ResolvedClusterLifecycle{}, err + } + + lifecycleMD := LifecycleAPIs{} + err = imagehelpers.GetLabel(lifecycleImg, lifecycleApisLabel, &deprecatedLifecycleMD) if err != nil { return buildapi.ResolvedClusterLifecycle{}, err } return buildapi.ResolvedClusterLifecycle{ - Version: lifecycleMd.LifecycleInfo.Version, + Version: deprecatedLifecycleMD.LifecycleInfo.Version, API: buildapi.LifecycleAPI{ - BuildpackVersion: lifecycleMd.API.BuildpackVersion, - PlatformVersion: lifecycleMd.API.PlatformVersion, + BuildpackVersion: deprecatedLifecycleMD.API.BuildpackVersion, + PlatformVersion: deprecatedLifecycleMD.API.PlatformVersion, }, APIs: buildapi.LifecycleAPIs{ Buildpack: buildapi.APIVersions{ - Deprecated: toBuildAPISet(lifecycleMd.APIs.Buildpack.Deprecated), - Supported: toBuildAPISet(lifecycleMd.APIs.Buildpack.Supported), + Deprecated: toBuildAPISet(lifecycleMD.Buildpack.Deprecated), + Supported: toBuildAPISet(lifecycleMD.Buildpack.Supported), }, Platform: buildapi.APIVersions{ - Deprecated: toBuildAPISet(lifecycleMd.APIs.Platform.Deprecated), - Supported: toBuildAPISet(lifecycleMd.APIs.Platform.Supported), + Deprecated: toBuildAPISet(lifecycleMD.Platform.Deprecated), + Supported: toBuildAPISet(lifecycleMD.Platform.Supported), }, }, }, nil From 38b4b8063adbe1e4d52c9a62572c4767820e6db6 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Thu, 6 Jun 2024 17:35:38 -0400 Subject: [PATCH 35/39] Trigger new builds on new lifecycle version, not commit Commit is not going to work because we can't get this information from the lifecycle image labels, it is only available after a build from the application image labels Signed-off-by: Natalie Arellano --- pkg/apis/build/v1alpha2/builder_resource.go | 2 +- .../build/v1alpha2/cluster_lifecycle_types.go | 1 - pkg/apis/build/v1alpha2/image_builds_test.go | 24 +++++------ pkg/cnb/create_builder.go | 1 - pkg/duckbuilder/duck_builder.go | 4 +- pkg/reconciler/image/build_required.go | 4 +- pkg/reconciler/image/build_required_test.go | 40 +++++++++---------- pkg/reconciler/image/image_test.go | 26 ++++++------ 8 files changed, 50 insertions(+), 52 deletions(-) diff --git a/pkg/apis/build/v1alpha2/builder_resource.go b/pkg/apis/build/v1alpha2/builder_resource.go index 22621b9ca..4c8ed3aac 100644 --- a/pkg/apis/build/v1alpha2/builder_resource.go +++ b/pkg/apis/build/v1alpha2/builder_resource.go @@ -10,7 +10,7 @@ type BuilderResource interface { UpToDate() bool BuildpackMetadata() corev1alpha1.BuildpackMetadataList RunImage() string - LifecycleCommit() string + LifecycleVersion() string GetKind() string ConditionReadyMessage() string } diff --git a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go index 9242ddce2..8d9847b6c 100644 --- a/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go +++ b/pkg/apis/build/v1alpha2/cluster_lifecycle_types.go @@ -42,7 +42,6 @@ type ClusterLifecycleStatus struct { // +k8s:openapi-gen=true type ResolvedClusterLifecycle struct { Version string `json:"version,omitempty"` - Commit string `json:"commit,omitempty"` // Deprecated: Use `LifecycleAPIs` instead API LifecycleAPI `json:"api,omitempty"` diff --git a/pkg/apis/build/v1alpha2/image_builds_test.go b/pkg/apis/build/v1alpha2/image_builds_test.go index f68f3e133..caa934c08 100644 --- a/pkg/apis/build/v1alpha2/image_builds_test.go +++ b/pkg/apis/build/v1alpha2/image_builds_test.go @@ -371,16 +371,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - Kind string - LatestImage string - LatestRunImage string - LatestLifecycleCommit string - Name string - Namespace string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + Kind string + LatestImage string + LatestRunImage string + LatestLifecycleVersion string + Name string + Namespace string } func (t TestBuilderResource) ConditionReadyMessage() string { @@ -410,8 +410,8 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } -func (t TestBuilderResource) LifecycleCommit() string { - return t.LatestLifecycleCommit +func (t TestBuilderResource) LifecycleVersion() string { + return t.LatestLifecycleVersion } func (t TestBuilderResource) GetName() string { diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index 6e6b37a42..ccd71748f 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -119,7 +119,6 @@ func (r *RemoteBuilderCreator) CreateBuilder( }, Lifecycle: buildapi.ResolvedClusterLifecycle{ // TODO: test Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, - Commit: clusterLifecycle.Status.ResolvedClusterLifecycle.Commit, API: clusterLifecycle.Status.ResolvedClusterLifecycle.API, APIs: clusterLifecycle.Status.ResolvedClusterLifecycle.APIs, }, diff --git a/pkg/duckbuilder/duck_builder.go b/pkg/duckbuilder/duck_builder.go index b5924f5a4..617e6fa91 100644 --- a/pkg/duckbuilder/duck_builder.go +++ b/pkg/duckbuilder/duck_builder.go @@ -57,8 +57,8 @@ func (b *DuckBuilder) RunImage() string { return b.Status.Stack.RunImage } -func (b *DuckBuilder) LifecycleCommit() string { - return b.Status.Lifecycle.Commit +func (b *DuckBuilder) LifecycleVersion() string { + return b.Status.Lifecycle.Version } func (b *DuckBuilder) ConditionReadyMessage() string { diff --git a/pkg/reconciler/image/build_required.go b/pkg/reconciler/image/build_required.go index db2c22e28..e4bf06797 100644 --- a/pkg/reconciler/image/build_required.go +++ b/pkg/reconciler/image/build_required.go @@ -138,7 +138,7 @@ func lifecycleChange(lastBuild *buildapi.Build, builder buildapi.BuilderResource return nil } - oldLifecycle := lastBuild.Status.LifecycleCommit - newLifecycle := builder.LifecycleCommit() + oldLifecycle := lastBuild.Status.LifecycleVersion + newLifecycle := builder.LifecycleVersion() return buildchange.NewLifecycleChange(oldLifecycle, newLifecycle) } diff --git a/pkg/reconciler/image/build_required_test.go b/pkg/reconciler/image/build_required_test.go index e5026b3d8..c669e67d5 100644 --- a/pkg/reconciler/image/build_required_test.go +++ b/pkg/reconciler/image/build_required_test.go @@ -63,9 +63,9 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { BuilderMetadata: []corev1alpha1.BuildpackMetadata{ {Id: "buildpack.matches", Version: "1"}, }, - LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", - LatestLifecycleCommit: "some-git-commit", - Kind: buildapi.BuilderKind, + LatestRunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + LatestLifecycleVersion: "some-version", + Kind: buildapi.BuilderKind, } latestBuild := &buildapi.Build{ @@ -93,8 +93,8 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { RunImage: "some.registry.io/run-image@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stack.bionic", }, - LatestImage: "some.registry.io/built@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", - LifecycleCommit: "some-git-commit", + LatestImage: "some.registry.io/built@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", + LifecycleVersion: "some-version", }, } @@ -401,14 +401,14 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { }) it("true if builder has a different lifecycle image", func() { - builder.LatestLifecycleCommit = "some-new-git-commit" + builder.LatestLifecycleVersion = "some-new-version" expectedChanges := testhelpers.CompactJSON(` [ { "reason": "LIFECYCLE", - "old": "some-git-commit", - "new": "some-new-git-commit" + "old": "some-version", + "new": "some-new-version" } ]`) @@ -821,16 +821,16 @@ func testImageBuilds(t *testing.T, when spec.G, it spec.S) { } type TestBuilderResource struct { - BuilderReady bool - BuilderUpToDate bool - BuilderMetadata []corev1alpha1.BuildpackMetadata - ImagePullSecrets []corev1.LocalObjectReference - LatestImage string - LatestRunImage string - LatestLifecycleCommit string - Name string - Namespace string - Kind string + BuilderReady bool + BuilderUpToDate bool + BuilderMetadata []corev1alpha1.BuildpackMetadata + ImagePullSecrets []corev1.LocalObjectReference + LatestImage string + LatestRunImage string + LatestLifecycleVersion string + Name string + Namespace string + Kind string } func (t TestBuilderResource) BuildBuilderSpec() corev1alpha1.BuildBuilderSpec { @@ -856,8 +856,8 @@ func (t TestBuilderResource) RunImage() string { return t.LatestRunImage } -func (t TestBuilderResource) LifecycleCommit() string { - return t.LatestLifecycleCommit +func (t TestBuilderResource) LifecycleVersion() string { + return t.LatestLifecycleVersion } func (t TestBuilderResource) GetName() string { diff --git a/pkg/reconciler/image/image_test.go b/pkg/reconciler/image/image_test.go index f3389be5e..7fb1dc138 100644 --- a/pkg/reconciler/image/image_test.go +++ b/pkg/reconciler/image/image_test.go @@ -167,7 +167,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Commit: "some-commit", + Version: "some-version", }, Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ @@ -1467,7 +1467,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1628,7 +1628,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -1742,7 +1742,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Commit: "some-commit", + Version: "some-version", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -1792,7 +1792,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -1920,7 +1920,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Commit: "some-commit", + Version: "some-version", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -1970,7 +1970,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "gcr.io/test-project/install/run@sha256:42841631725942db48b7ba8b788b97374a2ada34c84ee02ca5e02ef3d4b0dfca", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -2089,7 +2089,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { ID: "io.buildpacks.stacks.bionic", }, Lifecycle: buildapi.ResolvedClusterLifecycle{ - Commit: "some-new-commit", + Version: "some-new-version", }, BuilderMetadata: corev1alpha1.BuildpackMetadataList{ { @@ -2139,7 +2139,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", BuildMetadata: corev1alpha1.BuildpackMetadataList{ { Id: "io.buildpack", @@ -2172,8 +2172,8 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { [ { "reason": "LIFECYCLE", - "old": "some-commit", - "new": "some-new-commit" + "old": "some-version", + "new": "some-new-version" } ]`), }, @@ -2499,7 +2499,7 @@ func testImageReconciler(t *testing.T, when spec.G, it spec.S) { RunImage: "some/run@sha256:67e3de2af270bf09c02e9a644aeb7e87e6b3c049abe6766bf6b6c3728a83e7fb", ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ { @@ -2930,7 +2930,7 @@ func builds(image *buildapi.Image, sourceResolver *buildapi.SourceResolver, coun RunImage: runImageRef, ID: "io.buildpacks.stacks.bionic", }, - LifecycleCommit: "some-commit", + LifecycleVersion: "some-version", Status: corev1alpha1.Status{ Conditions: corev1alpha1.Conditions{ condition, From 34c7efc91e3ae2bb111e343daa886e86ac4d8277 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Mon, 17 Jun 2024 15:23:01 -0400 Subject: [PATCH 36/39] Remove hack/lifecycle Signed-off-by: Natalie Arellano --- hack/build.sh | 8 -- hack/lifecycle/main.go | 279 ----------------------------------------- 2 files changed, 287 deletions(-) delete mode 100644 hack/lifecycle/main.go diff --git a/hack/build.sh b/hack/build.sh index 696f4516a..b89754522 100644 --- a/hack/build.sh +++ b/hack/build.sh @@ -1,10 +1,5 @@ #!/bin/bash -function lifecycle_image_build() { - image=$1 - go run hack/lifecycle/main.go --tag=${image} -} - function generate_kbld_config_pack() { path=$1 registry=$2 @@ -195,9 +190,6 @@ function compile() { completion_image=${IMAGE_PREFIX}completion lifecycle_image=${IMAGE_PREFIX}lifecycle - echo "Building Lifecycle" - lifecycle_image_build ${lifecycle_image} - echo "Generating kbld config" temp_dir=$(mktemp -d) kbld_config_path="${temp_dir}/kbld-config" diff --git a/hack/lifecycle/main.go b/hack/lifecycle/main.go deleted file mode 100644 index b153468f4..000000000 --- a/hack/lifecycle/main.go +++ /dev/null @@ -1,279 +0,0 @@ -package main - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "flag" - "fmt" - "io" - "log" - "net/http" - "os" - "path" - "reflect" - "regexp" - "time" - - "github.com/BurntSushi/toml" - "github.com/buildpacks/imgutil/layer" - "github.com/google/go-containerregistry/pkg/authn" - v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/mutate" - "github.com/google/go-containerregistry/pkg/v1/random" - "github.com/google/go-containerregistry/pkg/v1/tarball" - "github.com/pkg/errors" - - "github.com/pivotal/kpack/pkg/cnb" - "github.com/pivotal/kpack/pkg/registry" - "github.com/pivotal/kpack/pkg/registry/imagehelpers" -) - -const ( - lifecycleMetadataLabel = "io.buildpacks.lifecycle.metadata" - lifecycleLocation = "/cnb/lifecycle/" - lifecycleVersion = "0.17.3" -) - -var ( - tag = flag.String("tag", "", "tag for lifecycle image") - - normalizedTime = time.Date(1980, time.January, 1, 0, 0, 1, 0, time.UTC) -) - -func main() { - flag.Parse() - - image, err := lifecycleImage( - fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+linux.x86-64.tgz", lifecycleVersion, lifecycleVersion), - fmt.Sprintf("https://github.com/buildpacks/lifecycle/releases/download/v%s/lifecycle-v%s+windows.x86-64.tgz", lifecycleVersion, lifecycleVersion), - ) - if err != nil { - log.Fatal(err) - } - - identifier, err := (®istry.Client{}).Save(authn.DefaultKeychain, *tag, image) - if err != nil { - log.Fatal(err) - } - - log.Println(fmt.Sprintf("saved lifecycle image: %s ", identifier)) - -} - -func lifecycleImage(linuxUrl, windowsUrl string) (v1.Image, error) { - image, err := random.Image(0, 0) - if err != nil { - return nil, err - } - - linuxDescriptor, err := lifecycleDescriptor(linuxUrl) - if err != nil { - return nil, err - } - windowsDescriptor, err := lifecycleDescriptor(windowsUrl) - if err != nil { - return nil, err - } - if !reflect.DeepEqual(linuxDescriptor, windowsDescriptor) { - return nil, errors.New("linux and windows lifecycle descriptors do not match. Check urls.") - } - - linuxLayer, err := lifecycleLayer(linuxUrl, "linux") - if err != nil { - return nil, err - } - linuxDiffID, err := linuxLayer.DiffID() - if err != nil { - return nil, err - } - - image, err = imagehelpers.SetStringLabel(image, "linux", linuxDiffID.String()) - if err != nil { - return nil, err - } - - windowsLayer, err := lifecycleLayer(windowsUrl, "windows") - if err != nil { - return nil, err - } - - windowsDiffID, err := windowsLayer.DiffID() - if err != nil { - return nil, err - } - - image, err = imagehelpers.SetStringLabel(image, "windows", windowsDiffID.String()) - if err != nil { - return nil, err - } - - image, err = mutate.AppendLayers(image, linuxLayer, windowsLayer) - if err != nil { - return nil, err - } - - return imagehelpers.SetLabels(image, map[string]interface{}{ - lifecycleMetadataLabel: lifecycleDescriptorToMetadata(linuxDescriptor), - }) -} - -func lifecycleDescriptor(url string) (cnb.LifecycleDescriptor, error) { - lr, err := lifecycleReader(url) - if err != nil { - return cnb.LifecycleDescriptor{}, err - } - defer lr.Close() - tr := tar.NewReader(lr) - for { - header, err := tr.Next() - if err != nil { - break - } - - name := header.Name - if name == "lifecycle.toml" { - descriptor := cnb.LifecycleDescriptor{} - if _, err := toml.NewDecoder(tr).Decode(&descriptor); err != nil { - return cnb.LifecycleDescriptor{}, err - } - return descriptor, nil - } - - continue - } - return cnb.LifecycleDescriptor{}, errors.New("could not find lifecycle descriptor lifecyle.toml") -} - -func lifecycleDescriptorToMetadata(descriptor cnb.LifecycleDescriptor) cnb.LifecycleMetadata { - return cnb.LifecycleMetadata{ - LifecycleInfo: descriptor.Info, - API: descriptor.API, - APIs: descriptor.APIs, - } -} - -func lifecycleLayer(url, os string) (v1.Layer, error) { - b := &bytes.Buffer{} - tw := newLayerWriter(b, os) - - err := tw.WriteHeader(&tar.Header{ - Typeflag: tar.TypeDir, - Name: lifecycleLocation, - Mode: 0755, - ModTime: normalizedTime, - }) - if err != nil { - return nil, err - } - - var regex = regexp.MustCompile(`^[^/]+/([^/]+)$`) - - lr, err := lifecycleReader(url) - if err != nil { - return nil, err - } - defer lr.Close() - tr := tar.NewReader(lr) - for { - header, err := tr.Next() - if err != nil { - break - } - - pathMatches := regex.FindStringSubmatch(path.Clean(header.Name)) - if pathMatches != nil { - binaryName := pathMatches[1] - - header.Name = lifecycleLocation + binaryName - header.ModTime = normalizedTime - err = tw.WriteHeader(header) - if err != nil { - return nil, err - } - - buf, err := io.ReadAll(tr) - if err != nil { - return nil, err - } - - _, err = tw.Write(buf) - if err != nil { - return nil, err - } - } - - } - - return tarball.LayerFromReader(b) -} - -func lifecycleReader(url string) (io.ReadCloser, error) { - dir, err := os.MkdirTemp("", "lifecycle") - if err != nil { - return nil, err - } - - lifecycleLocation := dir + "/lifecycle.tgz" - - err = download(lifecycleLocation, url) - if err != nil { - return nil, err - } - - file, err := os.Open(lifecycleLocation) - if err != nil { - return nil, err - } - - gzr, err := gzip.NewReader(file) - - return &ReadCloserWrapper{ - Reader: gzr, - closer: func() error { - defer os.RemoveAll(dir) - defer file.Close() - return gzr.Close() - }, - }, err -} - -func download(filepath string, url string) error { - resp, err := http.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - out, err := os.Create(filepath) - if err != nil { - return err - } - defer out.Close() - - _, err = io.Copy(out, resp.Body) - return err -} - -// copied from github.com/docker/docker/pkg/ioutils -type ReadCloserWrapper struct { - io.Reader - closer func() error -} - -func (r *ReadCloserWrapper) Close() error { - return r.closer() -} - -func newLayerWriter(fileWriter io.Writer, os string) layerWriter { - if os == "windows" { - return layer.NewWindowsWriter(fileWriter) - } - return tar.NewWriter(fileWriter) -} - -type layerWriter interface { - WriteHeader(hdr *tar.Header) error - Write(b []byte) (int, error) - Close() error -} From 920682e4dcd403958f6e98ec845483191114a06a Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Mon, 17 Jun 2024 15:24:22 -0400 Subject: [PATCH 37/39] Fix reading lifecycle metadata from image labels (and test!) Signed-off-by: Natalie Arellano --- pkg/cnb/buildpack_metadata.go | 2 +- pkg/cnb/remote_lifecycle_reader.go | 6 +- pkg/cnb/remote_lifecycle_reader_test.go | 79 +++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 pkg/cnb/remote_lifecycle_reader_test.go diff --git a/pkg/cnb/buildpack_metadata.go b/pkg/cnb/buildpack_metadata.go index ea2555b81..dbdeb50da 100644 --- a/pkg/cnb/buildpack_metadata.go +++ b/pkg/cnb/buildpack_metadata.go @@ -61,7 +61,7 @@ type LifecycleMetadata struct { } type LifecycleDescriptor struct { - Info LifecycleInfo `toml:"lifecycle"` + Info LifecycleInfo `toml:"lifecycle" json:"lifecycle,omitempty"` // Deprecated: Use `LifecycleAPIs` instead API LifecycleAPI `toml:"api" json:"api,omitempty"` diff --git a/pkg/cnb/remote_lifecycle_reader.go b/pkg/cnb/remote_lifecycle_reader.go index 14d1d5179..4a4172d85 100644 --- a/pkg/cnb/remote_lifecycle_reader.go +++ b/pkg/cnb/remote_lifecycle_reader.go @@ -19,20 +19,20 @@ func (r *RemoteLifecycleReader) Read(keychain authn.Keychain, clusterLifecycleSp return buildapi.ResolvedClusterLifecycle{}, err } - deprecatedLifecycleMD := LifecycleMetadata{} + deprecatedLifecycleMD := LifecycleDescriptor{} err = imagehelpers.GetLabel(lifecycleImg, lifecycleBuilderMetadataLabel, &deprecatedLifecycleMD) if err != nil { return buildapi.ResolvedClusterLifecycle{}, err } lifecycleMD := LifecycleAPIs{} - err = imagehelpers.GetLabel(lifecycleImg, lifecycleApisLabel, &deprecatedLifecycleMD) + err = imagehelpers.GetLabel(lifecycleImg, lifecycleApisLabel, &lifecycleMD) if err != nil { return buildapi.ResolvedClusterLifecycle{}, err } return buildapi.ResolvedClusterLifecycle{ - Version: deprecatedLifecycleMD.LifecycleInfo.Version, + Version: deprecatedLifecycleMD.Info.Version, API: buildapi.LifecycleAPI{ BuildpackVersion: deprecatedLifecycleMD.API.BuildpackVersion, PlatformVersion: deprecatedLifecycleMD.API.PlatformVersion, diff --git a/pkg/cnb/remote_lifecycle_reader_test.go b/pkg/cnb/remote_lifecycle_reader_test.go new file mode 100644 index 000000000..745457448 --- /dev/null +++ b/pkg/cnb/remote_lifecycle_reader_test.go @@ -0,0 +1,79 @@ +package cnb_test + +import ( + "fmt" + "testing" + + "github.com/google/go-containerregistry/pkg/authn" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/random" + "github.com/sclevine/spec" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + buildapi "github.com/pivotal/kpack/pkg/apis/build/v1alpha2" + corev1alpha1 "github.com/pivotal/kpack/pkg/apis/core/v1alpha1" + "github.com/pivotal/kpack/pkg/cnb" + "github.com/pivotal/kpack/pkg/registry/imagehelpers" + "github.com/pivotal/kpack/pkg/registry/registryfakes" +) + +func TestRemoteLifecycleReader(t *testing.T) { + spec.Run(t, "Test Lifecycle Reader", testRemoteLifecycleReader) +} + +func testRemoteLifecycleReader(t *testing.T, when spec.G, it spec.S) { + when("Remote Lifecycle Reader", func() { + const lifecycleTag = "gcr.io/image/lifecycle" + + var ( + fakeClient = registryfakes.NewFakeClient() + + expectedKeychain = authn.NewMultiKeychain(authn.DefaultKeychain) + remoteLifecycleReader = &cnb.RemoteLifecycleReader{ + RegistryClient: fakeClient, + } + ) + + it("returns lifecycle metadata", func() { + lifecycleImage := lifecycleImage(t, "some-version") + + fakeClient.AddImage(lifecycleTag, lifecycleImage, expectedKeychain) + + resolvedLifecycle, err := remoteLifecycleReader.Read(expectedKeychain, buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{ + Image: lifecycleTag, + }, + }) + require.NoError(t, err) + + assert.Equal(t, buildapi.ResolvedClusterLifecycle{ + Version: "some-version", + API: buildapi.LifecycleAPI{ + BuildpackVersion: "0.7", + PlatformVersion: "0.7", + }, + APIs: buildapi.LifecycleAPIs{ + Buildpack: buildapi.APIVersions{Supported: buildapi.APISet{"0.7", "0.8", "0.9", "0.10", "0.11"}}, + Platform: buildapi.APIVersions{Supported: buildapi.APISet{"0.7", "0.8", "0.9", "0.10", "0.11", "0.12", "0.13"}}, + }, + }, resolvedLifecycle) + }) + }) +} + +func lifecycleImage(t *testing.T, version string) v1.Image { + image, err := random.Image(10, 10) + require.NoError(t, err) + + image, err = imagehelpers.SetStringLabels( + image, + map[string]string{ + "io.buildpacks.builder.metadata": fmt.Sprintf("{\"lifecycle\":{\"version\":\"%s\"},\"api\":{\"buildpack\":\"0.7\",\"platform\":\"0.7\"}}", version), + "io.buildpacks.lifecycle.apis": "{\"buildpack\":{\"deprecated\":[],\"supported\":[\"0.7\",\"0.8\",\"0.9\",\"0.10\",\"0.11\"]},\"platform\":{\"deprecated\":[],\"supported\":[\"0.7\",\"0.8\",\"0.9\",\"0.10\",\"0.11\",\"0.12\",\"0.13\"]}}", + }, + ) + require.NoError(t, err) + + return image +} From cd708ac7d61e28a1ceba1629286ca227e4715d17 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Mon, 17 Jun 2024 17:06:58 -0400 Subject: [PATCH 38/39] Fix e2e tests maybe Signed-off-by: Natalie Arellano --- test/cosign_e2e_test.go | 48 ++++++++++++++++++++++++++++++++++++++ test/execute_build_test.go | 24 +++++++++++++++++++ test/slsa_test.go | 24 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/test/cosign_e2e_test.go b/test/cosign_e2e_test.go index dcee00132..f384c3f27 100644 --- a/test/cosign_e2e_test.go +++ b/test/cosign_e2e_test.go @@ -32,6 +32,7 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { buildpackName = "buildpack" clusterBuildpackName = "cluster-buildpack-cosign" clusterStackName = "stack-cosign" + clusterLifecycleName = "lifecycle-cosign" builderName = "custom-signed-builder" clusterBuilderName = "custom-signed-cluster-builder-cosign" cosignSecretName = "cosign-creds" @@ -73,6 +74,11 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { require.NoError(t, err) } + err = clients.client.KpackV1alpha2().ClusterLifecycles().Delete(ctx, clusterLifecycleName, metav1.DeleteOptions{}) + if !errors.IsNotFound(err) { + require.NoError(t, err) + } + err = clients.client.KpackV1alpha2().ClusterBuilders().Delete(ctx, clusterBuilderName, metav1.DeleteOptions{}) if !errors.IsNotFound(err) { require.NoError(t, err) @@ -175,6 +181,16 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { }, }, metav1.CreateOptions{}) require.NoError(t, err) + + _, err = clients.client.KpackV1alpha2().ClusterLifecycles().Create(ctx, &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterLifecycleName, + }, + Spec: buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{Image: "buildpacksio/lifecycle"}, + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) }) it("Signs a Builder image successfully when the key is not password-protected", func() { @@ -205,6 +221,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -322,6 +342,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -447,6 +471,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -575,6 +603,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -691,6 +723,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -805,6 +841,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -928,6 +968,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -1053,6 +1097,10 @@ func testSignBuilder(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", diff --git a/test/execute_build_test.go b/test/execute_build_test.go index ef8c2ccd4..08c51765f 100644 --- a/test/execute_build_test.go +++ b/test/execute_build_test.go @@ -49,6 +49,7 @@ func testCreateImage(t *testing.T, _ spec.G, it spec.S) { buildpackName = "buildpack" clusterBuildpackName = "cluster-buildpack" clusterStackName = "stack" + clusterLifecycleName = "lifecycle" builderName = "custom-builder" clusterBuilderName = "custom-cluster-builder" ) @@ -154,6 +155,11 @@ func testCreateImage(t *testing.T, _ spec.G, it spec.S) { require.NoError(t, err) } + err = clients.client.KpackV1alpha2().ClusterLifecycles().Delete(ctx, clusterLifecycleName, metav1.DeleteOptions{}) + if !errors.IsNotFound(err) { + require.NoError(t, err) + } + err = clients.client.KpackV1alpha2().ClusterBuilders().Delete(ctx, clusterBuilderName, metav1.DeleteOptions{}) if !errors.IsNotFound(err) { require.NoError(t, err) @@ -258,6 +264,16 @@ func testCreateImage(t *testing.T, _ spec.G, it spec.S) { }, metav1.CreateOptions{}) require.NoError(t, err) + _, err = clients.client.KpackV1alpha2().ClusterLifecycles().Create(ctx, &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterLifecycleName, + }, + Spec: buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{Image: "buildpacksio/lifecycle"}, + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) + builder, err := clients.client.KpackV1alpha2().Builders(testNamespace).Create(ctx, &buildapi.Builder{ ObjectMeta: metav1.ObjectMeta{ Name: builderName, @@ -270,6 +286,10 @@ func testCreateImage(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -366,6 +386,10 @@ func testCreateImage(t *testing.T, _ spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", diff --git a/test/slsa_test.go b/test/slsa_test.go index a1bb6fd15..3e91184d7 100644 --- a/test/slsa_test.go +++ b/test/slsa_test.go @@ -60,6 +60,7 @@ func testSlsaBuild(t *testing.T, when spec.G, it spec.S) { buildpackName = "buildpack" clusterBuildpackName = "cluster-buildpack-slsa" clusterStackName = "stack-slsa" + clusterLifecycleName = "lifecycle-slsa" builderName = "custom-builder" clusterBuilderName = "custom-cluster-builder-slsa" cosignSecretName = "cosign-creds" @@ -100,6 +101,11 @@ func testSlsaBuild(t *testing.T, when spec.G, it spec.S) { require.NoError(t, err) } + err = clients.client.KpackV1alpha2().ClusterLifecycles().Delete(ctx, clusterLifecycleName, metav1.DeleteOptions{}) + if !errors.IsNotFound(err) { + require.NoError(t, err) + } + err = clients.client.KpackV1alpha2().ClusterBuilders().Delete(ctx, clusterBuilderName, metav1.DeleteOptions{}) if !errors.IsNotFound(err) { require.NoError(t, err) @@ -204,6 +210,16 @@ func testSlsaBuild(t *testing.T, when spec.G, it spec.S) { }, metav1.CreateOptions{}) require.NoError(t, err) + _, err = clients.client.KpackV1alpha2().ClusterLifecycles().Create(ctx, &buildapi.ClusterLifecycle{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterLifecycleName, + }, + Spec: buildapi.ClusterLifecycleSpec{ + ImageSource: corev1alpha1.ImageSource{Image: "buildpacksio/lifecycle"}, + }, + }, metav1.CreateOptions{}) + require.NoError(t, err) + builder, err := clients.client.KpackV1alpha2().Builders(testNamespace).Create(ctx, &buildapi.Builder{ ObjectMeta: metav1.ObjectMeta{ Name: builderName, @@ -216,6 +232,10 @@ func testSlsaBuild(t *testing.T, when spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", @@ -312,6 +332,10 @@ func testSlsaBuild(t *testing.T, when spec.G, it spec.S) { Name: clusterStackName, Kind: "ClusterStack", }, + Lifecycle: corev1.ObjectReference{ + Name: clusterLifecycleName, + Kind: "ClusterLifecycle", + }, Store: corev1.ObjectReference{ Name: clusterStoreName, Kind: "ClusterStore", From 3377b79b4107b87ba2116866c0fb63fb96385659 Mon Sep 17 00:00:00 2001 From: Natalie Arellano Date: Tue, 18 Jun 2024 11:19:27 -0400 Subject: [PATCH 39/39] Add unit test Signed-off-by: Natalie Arellano --- pkg/cnb/create_builder.go | 4 ++-- pkg/cnb/create_builder_test.go | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/cnb/create_builder.go b/pkg/cnb/create_builder.go index ccd71748f..cd86886ce 100644 --- a/pkg/cnb/create_builder.go +++ b/pkg/cnb/create_builder.go @@ -45,7 +45,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( if err != nil { return buildapi.BuilderRecord{}, err } - lifecycleImage, _, err := r.RegistryClient.Fetch(lifecycleKeychain, clusterLifecycle.Spec.Image) // TODO: should there be a "latest image" on the status? + lifecycleImage, _, err := r.RegistryClient.Fetch(lifecycleKeychain, clusterLifecycle.Spec.Image) // TODO: confirm, should there be a "latest image" on the status? if err != nil { return buildapi.BuilderRecord{}, err } @@ -117,7 +117,7 @@ func (r *RemoteBuilderCreator) CreateBuilder( RunImage: relocatedRunImage, ID: clusterStack.Status.Id, }, - Lifecycle: buildapi.ResolvedClusterLifecycle{ // TODO: test + Lifecycle: buildapi.ResolvedClusterLifecycle{ Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, API: clusterLifecycle.Status.ResolvedClusterLifecycle.API, APIs: clusterLifecycle.Status.ResolvedClusterLifecycle.APIs, diff --git a/pkg/cnb/create_builder_test.go b/pkg/cnb/create_builder_test.go index 3efbdf002..dc8ed1c8c 100644 --- a/pkg/cnb/create_builder_test.go +++ b/pkg/cnb/create_builder_test.go @@ -380,6 +380,11 @@ func testCreateBuilderOs(os string, t *testing.T, when spec.G, it spec.S) { assert.Equal(t, int64(10), builderRecord.ObservedStoreGeneration) assert.Equal(t, int64(11), builderRecord.ObservedStackGeneration) assert.Equal(t, os, builderRecord.OS) + assert.Equal(t, builderRecord.Lifecycle, buildapi.ResolvedClusterLifecycle{ + Version: clusterLifecycle.Status.ResolvedClusterLifecycle.Version, + API: clusterLifecycle.Status.ResolvedClusterLifecycle.API, + APIs: clusterLifecycle.Status.ResolvedClusterLifecycle.APIs, + }) assert.Equal(t, builderRecord.Order, []corev1alpha1.OrderEntry{ {