diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 5b4da5a70b5..554463414f8 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -112,6 +112,7 @@ func main() { buildInformerFactory := buildinformers.NewSharedInformerFactory(buildClient, opt.ResyncPeriod) taskInformer := pipelineInformerFactory.Pipeline().V1alpha1().Tasks() + clusterTaskInformer := pipelineInformerFactory.Pipeline().V1alpha1().ClusterTasks() taskRunInformer := pipelineInformerFactory.Pipeline().V1alpha1().TaskRuns() resourceInformer := pipelineInformerFactory.Pipeline().V1alpha1().PipelineResources() buildInformer := buildInformerFactory.Build().V1alpha1().Builds() @@ -124,6 +125,7 @@ func main() { taskrun.NewController(opt, taskRunInformer, taskInformer, + clusterTaskInformer, buildInformer, resourceInformer, ), @@ -131,6 +133,7 @@ func main() { pipelineRunInformer, pipelineInformer, taskInformer, + clusterTaskInformer, taskRunInformer, resourceInformer, ), @@ -150,6 +153,7 @@ func main() { logger.Info("Waiting for informer caches to sync") for i, synced := range []cache.InformerSynced{ taskInformer.Informer().HasSynced, + clusterTaskInformer.Informer().HasSynced, taskRunInformer.Informer().HasSynced, buildInformer.Informer().HasSynced, resourceInformer.Informer().HasSynced, diff --git a/config/200-clusterrole.yaml b/config/200-clusterrole.yaml index d02f0f2201b..941ccebdaf5 100644 --- a/config/200-clusterrole.yaml +++ b/config/200-clusterrole.yaml @@ -16,7 +16,7 @@ rules: resources: ["customresourcedefinitions"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - apiGroups: ["pipeline.knative.dev"] - resources: ["tasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources"] + resources: ["tasks", "clustertasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - apiGroups: ["build.knative.dev"] resources: ["builds", "buildtemplates", "clusterbuildtemplates"] diff --git a/config/300-clustertask.yaml b/config/300-clustertask.yaml new file mode 100644 index 00000000000..875ff09f945 --- /dev/null +++ b/config/300-clustertask.yaml @@ -0,0 +1,18 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + creationTimestamp: null + labels: + controller-tools.k8s.io: "1.0" + name: clustertasks.pipeline.knative.dev +spec: + group: pipeline.knative.dev + names: + kind: ClusterTask + plural: clustertasks + categories: + - all + - knative + - build-pipeline + scope: Cluster + version: v1alpha1 diff --git a/docs/Concepts.md b/docs/Concepts.md index 50d89a6df78..55fee28f7b7 100644 --- a/docs/Concepts.md +++ b/docs/Concepts.md @@ -107,6 +107,10 @@ To run a step needs to pull an `Entrypoint` image. Maybe the image is hard to pu in your environment, so we provide a way for you to configure that by edit the `image`'s value in a configmap named [`config-entrypoint`](./../config/config-entrypoint.yaml). +### ClusterTask + +Similar to `Tasks` but but with a cluster scope. + ### Pipeline `Pipelines` describes a graph of [Tasks](#Task) to execute. diff --git a/docs/using.md b/docs/using.md index 28c0c7c339d..e36c7253a18 100644 --- a/docs/using.md +++ b/docs/using.md @@ -131,6 +131,29 @@ To access a `Param`, replace `resources` with `params` as below: ${inputs.params.NAME} ``` +## Cluster Task + +Similar to Task, but with a cluster scope. + +In case of using a ClusterTask, the `TaskRef` kind should be added. The default kind is Task +which represents a namespaced Task + +```yaml +apiVersion: pipeline.knative.dev/v1alpha1 +kind: Pipeline +metadata: + name: demo-pipeline + namespace: default +spec: + tasks: + - name: build-skaffold-web + taskRef: + name: build-push + kind: ClusterTask + params: + .... +``` + ## Running a Pipeline In order to run a Pipeline, you will need to provide: diff --git a/pkg/apis/pipeline/v1alpha1/cluster_task_types.go b/pkg/apis/pipeline/v1alpha1/cluster_task_types.go new file mode 100644 index 00000000000..449293a7975 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/cluster_task_types.go @@ -0,0 +1,72 @@ +/* +Copyright 2018 The Knative 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. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis" + "github.com/knative/pkg/webhook" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (t *ClusterTask) TaskSpec() TaskSpec { + return t.Spec +} + +func (t *ClusterTask) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +func (t *ClusterTask) Copy() TaskInterface { + return t.DeepCopy() +} + +func (t *ClusterTask) SetDefaults() { + t.Spec.SetDefaults() +} + +// Check that Task may be validated and defaulted. +var _ apis.Validatable = (*ClusterTask)(nil) +var _ apis.Defaultable = (*ClusterTask)(nil) + +// Assert that Task implements the GenericCRD interface. +var _ webhook.GenericCRD = (*ClusterTask)(nil) + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTask is a Task with a cluster scope +type ClusterTask struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the Task from the client + // +optional + Spec TaskSpec `json:"spec,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterTaskList contains a list of ClusterTask +type ClusterTaskList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Task `json:"items"` +} diff --git a/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go b/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go new file mode 100644 index 00000000000..24226b52e3c --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/cluster_task_validation.go @@ -0,0 +1,28 @@ +/* +Copyright 2018 The Knative 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. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis" +) + +func (t *ClusterTask) Validate() *apis.FieldError { + if err := validateObjectMetadata(t.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + return t.Spec.Validate() +} diff --git a/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go b/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go index 6a7bdd23d89..6433f470490 100644 --- a/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go +++ b/pkg/apis/pipeline/v1alpha1/pipeline_defaults.go @@ -20,4 +20,10 @@ func (p *Pipeline) SetDefaults() { p.Spec.SetDefaults() } -func (ps *PipelineSpec) SetDefaults() {} +func (ps *PipelineSpec) SetDefaults() { + for _, pt := range ps.Tasks { + if pt.TaskRef.Kind == "" { + pt.TaskRef.Kind = NamespacedTaskKind + } + } +} diff --git a/pkg/apis/pipeline/v1alpha1/pipeline_types.go b/pkg/apis/pipeline/v1alpha1/pipeline_types.go index 4c94ec8291d..d4b3fb08cb6 100644 --- a/pkg/apis/pipeline/v1alpha1/pipeline_types.go +++ b/pkg/apis/pipeline/v1alpha1/pipeline_types.go @@ -37,6 +37,16 @@ type PipelineStatus struct { var _ apis.Validatable = (*Pipeline)(nil) var _ apis.Defaultable = (*Pipeline)(nil) +// TaskKind defines the type of Task used by the pipeline. +type TaskKind string + +const ( + // NamespacedTaskKind indicates that the task type has a namepace scope. + NamespacedTaskKind TaskKind = "Task" + // ClusterTaskKind indicates that task type has a cluster scope. + ClusterTaskKind TaskKind = "ClusterTask" +) + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -90,6 +100,8 @@ type ResourceDependency struct { type TaskRef struct { // Name of the referent; More info: http://kubernetes.io/docs/user-guide/identifiers#names Name string `json:"name"` + // TaskKind inficates the kind of the task, namespaced or cluster scoped. + Kind TaskKind `json:"kind,omitempty"` // API version of the referent // +optional APIVersion string `json:"apiVersion,omitempty"` diff --git a/pkg/apis/pipeline/v1alpha1/register.go b/pkg/apis/pipeline/v1alpha1/register.go index 0a2d012f291..bea986dfa6a 100644 --- a/pkg/apis/pipeline/v1alpha1/register.go +++ b/pkg/apis/pipeline/v1alpha1/register.go @@ -48,6 +48,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Task{}, &TaskList{}, + &ClusterTask{}, + &ClusterTaskList{}, &TaskRun{}, &TaskRunList{}, &Pipeline{}, diff --git a/pkg/apis/pipeline/v1alpha1/task_interface.go b/pkg/apis/pipeline/v1alpha1/task_interface.go new file mode 100644 index 00000000000..44fab721be6 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/task_interface.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 The Knative 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. +*/ + +package v1alpha1 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// TaskInterface is implemented by Task and ClusterTask +type TaskInterface interface { + TaskMetadata() metav1.ObjectMeta + TaskSpec() TaskSpec + Copy() TaskInterface +} diff --git a/pkg/apis/pipeline/v1alpha1/task_types.go b/pkg/apis/pipeline/v1alpha1/task_types.go index 633c8d9fc56..e68580ff8ff 100644 --- a/pkg/apis/pipeline/v1alpha1/task_types.go +++ b/pkg/apis/pipeline/v1alpha1/task_types.go @@ -25,6 +25,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func (t *Task) TaskSpec() TaskSpec { + return t.Spec +} + +func (t *Task) TaskMetadata() metav1.ObjectMeta { + return t.ObjectMeta +} + +func (t *Task) Copy() TaskInterface { + return t.DeepCopy() +} + // TaskSpec defines the desired state of Task type TaskSpec struct { // +optional @@ -62,12 +74,6 @@ type TaskSpec struct { Affinity *corev1.Affinity `json:"affinity,omitempty"` } -// TaskStatus does not contain anything because Tasks on their own -// do not have a status, they just hold data which is later used by a -// TaskRun. -type TaskStatus struct { -} - // Check that Task may be validated and defaulted. var _ apis.Validatable = (*Task)(nil) var _ apis.Defaultable = (*Task)(nil) @@ -76,6 +82,7 @@ var _ apis.Defaultable = (*Task)(nil) var _ webhook.GenericCRD = (*Task)(nil) // +genclient +// +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Task is the Schema for the tasks API @@ -88,9 +95,6 @@ type Task struct { // Spec holds the desired state of the Task from the client // +optional Spec TaskSpec `json:"spec,omitempty"` - // Status communicates the observed state of the Task from the controller - // +optional - Status TaskStatus `json:"status,omitempty"` } // Inputs are the requirements that a task needs to run a Build. diff --git a/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go b/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go new file mode 100644 index 00000000000..d4a74e37e77 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/taskrun_defaults.go @@ -0,0 +1,27 @@ +/* +Copyright 2018 The Knative 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. +*/ + +package v1alpha1 + +func (tr *TaskRun) SetDefaults() { + tr.Spec.SetDefaults() +} + +func (trs *TaskRunSpec) SetDefaults() { + if trs.TaskRef.Kind == "" { + trs.TaskRef.Kind = NamespacedTaskKind + } +} diff --git a/pkg/apis/pipeline/v1alpha1/taskrun_types.go b/pkg/apis/pipeline/v1alpha1/taskrun_types.go index 0078b20a99b..c71c5c248d8 100644 --- a/pkg/apis/pipeline/v1alpha1/taskrun_types.go +++ b/pkg/apis/pipeline/v1alpha1/taskrun_types.go @@ -174,8 +174,6 @@ type TaskRunList struct { Items []TaskRun `json:"items"` } -func (tr *TaskRun) SetDefaults() {} - // GetBuildRef for task func (tr *TaskRun) GetBuildRef() corev1.ObjectReference { return corev1.ObjectReference{ diff --git a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go index a3866e69d12..a59d46c9d53 100644 --- a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go @@ -53,6 +53,66 @@ func (in *ClusterResource) DeepCopy() *ClusterResource { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterTask) DeepCopyInto(out *ClusterTask) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTask. +func (in *ClusterTask) DeepCopy() *ClusterTask { + if in == nil { + return nil + } + out := new(ClusterTask) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTask) 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 *ClusterTaskList) DeepCopyInto(out *ClusterTaskList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterTaskList. +func (in *ClusterTaskList) DeepCopy() *ClusterTaskList { + if in == nil { + return nil + } + out := new(ClusterTaskList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterTaskList) 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 *GitResource) DeepCopyInto(out *GitResource) { *out = *in @@ -707,7 +767,6 @@ func (in *Task) DeepCopyInto(out *Task) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status return } @@ -1125,22 +1184,6 @@ func (in *TaskSpec) DeepCopy() *TaskSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TaskStatus) DeepCopyInto(out *TaskStatus) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskStatus. -func (in *TaskStatus) DeepCopy() *TaskStatus { - if in == nil { - return nil - } - out := new(TaskStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TaskTrigger) DeepCopyInto(out *TaskTrigger) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go new file mode 100644 index 00000000000..f8d0881eb1c --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,144 @@ +/* +Copyright 2018 The Knative 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. +*/ +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/knative/build-pipeline/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" +) + +// ClusterTasksGetter has a method to return a ClusterTaskInterface. +// A group's client should implement this interface. +type ClusterTasksGetter interface { + ClusterTasks() ClusterTaskInterface +} + +// ClusterTaskInterface has methods to work with ClusterTask resources. +type ClusterTaskInterface interface { + Create(*v1alpha1.ClusterTask) (*v1alpha1.ClusterTask, error) + Update(*v1alpha1.ClusterTask) (*v1alpha1.ClusterTask, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.ClusterTask, error) + List(opts v1.ListOptions) (*v1alpha1.ClusterTaskList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterTask, err error) + ClusterTaskExpansion +} + +// clusterTasks implements ClusterTaskInterface +type clusterTasks struct { + client rest.Interface +} + +// newClusterTasks returns a ClusterTasks +func newClusterTasks(c *PipelineV1alpha1Client) *clusterTasks { + return &clusterTasks{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterTask, and returns the corresponding clusterTask object, and an error if there is any. +func (c *clusterTasks) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Get(). + Resource("clustertasks"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterTasks that match those selectors. +func (c *clusterTasks) List(opts v1.ListOptions) (result *v1alpha1.ClusterTaskList, err error) { + result = &v1alpha1.ClusterTaskList{} + err = c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterTasks. +func (c *clusterTasks) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Resource("clustertasks"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a clusterTask and creates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Create(clusterTask *v1alpha1.ClusterTask) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Post(). + Resource("clustertasks"). + Body(clusterTask). + Do(). + Into(result) + return +} + +// Update takes the representation of a clusterTask and updates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *clusterTasks) Update(clusterTask *v1alpha1.ClusterTask) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Put(). + Resource("clustertasks"). + Name(clusterTask.Name). + Body(clusterTask). + Do(). + Into(result) + return +} + +// Delete takes name of the clusterTask and deletes it. Returns an error if one occurs. +func (c *clusterTasks) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clustertasks"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterTasks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Resource("clustertasks"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched clusterTask. +func (c *clusterTasks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterTask, err error) { + result = &v1alpha1.ClusterTask{} + err = c.client.Patch(pt). + Resource("clustertasks"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_clustertask.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_clustertask.go new file mode 100644 index 00000000000..7046068047f --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_clustertask.go @@ -0,0 +1,117 @@ +/* +Copyright 2018 The Knative 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. +*/ +package fake + +import ( + v1alpha1 "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" + 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" +) + +// FakeClusterTasks implements ClusterTaskInterface +type FakeClusterTasks struct { + Fake *FakePipelineV1alpha1 +} + +var clustertasksResource = schema.GroupVersionResource{Group: "pipeline.knative.dev", Version: "v1alpha1", Resource: "clustertasks"} + +var clustertasksKind = schema.GroupVersionKind{Group: "pipeline.knative.dev", Version: "v1alpha1", Kind: "ClusterTask"} + +// Get takes name of the clusterTask, and returns the corresponding clusterTask object, and an error if there is any. +func (c *FakeClusterTasks) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterTask, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(clustertasksResource, name), &v1alpha1.ClusterTask{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterTask), err +} + +// List takes label and field selectors, and returns the list of ClusterTasks that match those selectors. +func (c *FakeClusterTasks) List(opts v1.ListOptions) (result *v1alpha1.ClusterTaskList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(clustertasksResource, clustertasksKind, opts), &v1alpha1.ClusterTaskList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterTaskList{ListMeta: obj.(*v1alpha1.ClusterTaskList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterTaskList).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 clusterTasks. +func (c *FakeClusterTasks) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(clustertasksResource, opts)) +} + +// Create takes the representation of a clusterTask and creates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *FakeClusterTasks) Create(clusterTask *v1alpha1.ClusterTask) (result *v1alpha1.ClusterTask, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(clustertasksResource, clusterTask), &v1alpha1.ClusterTask{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterTask), err +} + +// Update takes the representation of a clusterTask and updates it. Returns the server's representation of the clusterTask, and an error, if there is any. +func (c *FakeClusterTasks) Update(clusterTask *v1alpha1.ClusterTask) (result *v1alpha1.ClusterTask, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(clustertasksResource, clusterTask), &v1alpha1.ClusterTask{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterTask), err +} + +// Delete takes name of the clusterTask and deletes it. Returns an error if one occurs. +func (c *FakeClusterTasks) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(clustertasksResource, name), &v1alpha1.ClusterTask{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterTasks) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(clustertasksResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterTaskList{}) + return err +} + +// Patch applies the patch and returns the patched clusterTask. +func (c *FakeClusterTasks) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterTask, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(clustertasksResource, name, data, subresources...), &v1alpha1.ClusterTask{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterTask), err +} diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_pipeline_client.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_pipeline_client.go index 6a9d52637af..d8d72fc2ab0 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_pipeline_client.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_pipeline_client.go @@ -25,6 +25,10 @@ type FakePipelineV1alpha1 struct { *testing.Fake } +func (c *FakePipelineV1alpha1) ClusterTasks() v1alpha1.ClusterTaskInterface { + return &FakeClusterTasks{c} +} + func (c *FakePipelineV1alpha1) Pipelines(namespace string) v1alpha1.PipelineInterface { return &FakePipelines{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_task.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_task.go index 0750b69d6f7..952ab1d01ac 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_task.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_task.go @@ -97,18 +97,6 @@ func (c *FakeTasks) Update(task *v1alpha1.Task) (result *v1alpha1.Task, err erro return obj.(*v1alpha1.Task), 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 *FakeTasks) UpdateStatus(task *v1alpha1.Task) (*v1alpha1.Task, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(tasksResource, "status", c.ns, task), &v1alpha1.Task{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Task), err -} - // Delete takes name of the task and deletes it. Returns an error if one occurs. func (c *FakeTasks) Delete(name string, options *v1.DeleteOptions) error { _, err := c.Fake. diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go index 51712ca93b0..57d18e114f6 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go @@ -15,6 +15,8 @@ limitations under the License. */ package v1alpha1 +type ClusterTaskExpansion interface{} + type PipelineExpansion interface{} type PipelineResourceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go index 87568e4ef19..1c039cc8782 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go @@ -24,6 +24,7 @@ import ( type PipelineV1alpha1Interface interface { RESTClient() rest.Interface + ClusterTasksGetter PipelinesGetter PipelineResourcesGetter PipelineRunsGetter @@ -36,6 +37,10 @@ type PipelineV1alpha1Client struct { restClient rest.Interface } +func (c *PipelineV1alpha1Client) ClusterTasks() ClusterTaskInterface { + return newClusterTasks(c) +} + func (c *PipelineV1alpha1Client) Pipelines(namespace string) PipelineInterface { return newPipelines(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go index 450829968a0..6b61ed2ae38 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/task.go @@ -34,7 +34,6 @@ type TasksGetter interface { type TaskInterface interface { Create(*v1alpha1.Task) (*v1alpha1.Task, error) Update(*v1alpha1.Task) (*v1alpha1.Task, error) - UpdateStatus(*v1alpha1.Task) (*v1alpha1.Task, error) Delete(name string, options *v1.DeleteOptions) error DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error Get(name string, options v1.GetOptions) (*v1alpha1.Task, error) @@ -118,22 +117,6 @@ func (c *tasks) Update(task *v1alpha1.Task) (result *v1alpha1.Task, err error) { 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 *tasks) UpdateStatus(task *v1alpha1.Task) (result *v1alpha1.Task, err error) { - result = &v1alpha1.Task{} - err = c.client.Put(). - Namespace(c.ns). - Resource("tasks"). - Name(task.Name). - SubResource("status"). - Body(task). - Do(). - Into(result) - return -} - // Delete takes name of the task and deletes it. Returns an error if one occurs. func (c *tasks) Delete(name string, options *v1.DeleteOptions) error { return c.client.Delete(). diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 46c5dee5f8f..67ce9cd4361 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -50,6 +50,8 @@ func (f *genericInformer) Lister() cache.GenericLister { func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { // Group=pipeline.knative.dev, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("clustertasks"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Pipeline().V1alpha1().ClusterTasks().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("pipelines"): return &genericInformer{resource: resource.GroupResource(), informer: f.Pipeline().V1alpha1().Pipelines().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("pipelineresources"): diff --git a/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go b/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go new file mode 100644 index 00000000000..7366e31de60 --- /dev/null +++ b/pkg/client/informers/externalversions/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,85 @@ +/* +Copyright 2018 The Knative 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. +*/ +package v1alpha1 + +import ( + time "time" + + pipeline_v1alpha1 "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/knative/build-pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/knative/build-pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/knative/build-pipeline/pkg/client/listers/pipeline/v1alpha1" + 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" +) + +// ClusterTaskInformer provides access to a shared informer and lister for +// ClusterTasks. +type ClusterTaskInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.ClusterTaskLister +} + +type clusterTaskInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// NewClusterTaskInformer constructs a new informer for ClusterTask 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 NewClusterTaskInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, indexers, nil) +} + +// NewFilteredClusterTaskInformer constructs a new informer for ClusterTask 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 NewFilteredClusterTaskInformer(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.PipelineV1alpha1().ClusterTasks().List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PipelineV1alpha1().ClusterTasks().Watch(options) + }, + }, + &pipeline_v1alpha1.ClusterTask{}, + resyncPeriod, + indexers, + ) +} + +func (f *clusterTaskInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterTaskInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *clusterTaskInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipeline_v1alpha1.ClusterTask{}, f.defaultInformer) +} + +func (f *clusterTaskInformer) Lister() v1alpha1.ClusterTaskLister { + return v1alpha1.NewClusterTaskLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go b/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go index 56f957b6a68..98383d28a65 100644 --- a/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go @@ -21,6 +21,8 @@ import ( // Interface provides access to all the informers in this group version. type Interface interface { + // ClusterTasks returns a ClusterTaskInformer. + ClusterTasks() ClusterTaskInformer // Pipelines returns a PipelineInformer. Pipelines() PipelineInformer // PipelineResources returns a PipelineResourceInformer. @@ -44,6 +46,11 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} } +// ClusterTasks returns a ClusterTaskInformer. +func (v *version) ClusterTasks() ClusterTaskInformer { + return &clusterTaskInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +} + // Pipelines returns a PipelineInformer. func (v *version) Pipelines() PipelineInformer { return &pipelineInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/listers/pipeline/v1alpha1/clustertask.go b/pkg/client/listers/pipeline/v1alpha1/clustertask.go new file mode 100644 index 00000000000..0fc7260add8 --- /dev/null +++ b/pkg/client/listers/pipeline/v1alpha1/clustertask.go @@ -0,0 +1,62 @@ +/* +Copyright 2018 The Knative 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. +*/ +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterTaskLister helps list ClusterTasks. +type ClusterTaskLister interface { + // List lists all ClusterTasks in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.ClusterTask, err error) + // Get retrieves the ClusterTask from the index for a given name. + Get(name string) (*v1alpha1.ClusterTask, error) + ClusterTaskListerExpansion +} + +// clusterTaskLister implements the ClusterTaskLister interface. +type clusterTaskLister struct { + indexer cache.Indexer +} + +// NewClusterTaskLister returns a new ClusterTaskLister. +func NewClusterTaskLister(indexer cache.Indexer) ClusterTaskLister { + return &clusterTaskLister{indexer: indexer} +} + +// List lists all ClusterTasks in the indexer. +func (s *clusterTaskLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterTask, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterTask)) + }) + return ret, err +} + +// Get retrieves the ClusterTask from the index for a given name. +func (s *clusterTaskLister) Get(name string) (*v1alpha1.ClusterTask, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clustertask"), name) + } + return obj.(*v1alpha1.ClusterTask), nil +} diff --git a/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go b/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go index 79bf4b32ad2..b08a93f24d9 100644 --- a/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go @@ -15,6 +15,10 @@ limitations under the License. */ package v1alpha1 +// ClusterTaskListerExpansion allows custom methods to be added to +// ClusterTaskLister. +type ClusterTaskListerExpansion interface{} + // PipelineListerExpansion allows custom methods to be added to // PipelineLister. type PipelineListerExpansion interface{} diff --git a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go index 5918cff5023..978b4466dc7 100644 --- a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go +++ b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go @@ -69,6 +69,7 @@ type Reconciler struct { pipelineLister listers.PipelineLister taskRunLister listers.TaskRunLister taskLister listers.TaskLister + clusterTaskLister listers.ClusterTaskLister resourceLister listers.PipelineResourceLister tracker tracker.Interface } @@ -82,6 +83,7 @@ func NewController( pipelineRunInformer informers.PipelineRunInformer, pipelineInformer informers.PipelineInformer, taskInformer informers.TaskInformer, + clusterTaskInformer informers.ClusterTaskInformer, taskRunInformer informers.TaskRunInformer, resourceInformer informers.PipelineResourceInformer, ) *controller.Impl { @@ -91,6 +93,7 @@ func NewController( pipelineRunLister: pipelineRunInformer.Lister(), pipelineLister: pipelineInformer.Lister(), taskLister: taskInformer.Lister(), + clusterTaskLister: clusterTaskInformer.Lister(), taskRunLister: taskRunInformer.Lister(), resourceLister: resourceInformer.Lister(), } @@ -170,7 +173,12 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er return nil } pipelineState, err := resources.ResolvePipelineRun( - c.taskLister.Tasks(pr.Namespace).Get, + func(name string) (v1alpha1.TaskInterface, error) { + return c.taskLister.Tasks(pr.Namespace).Get(name) + }, + func(name string) (v1alpha1.TaskInterface, error) { + return c.clusterTaskLister.Get(name) + }, c.resourceLister.PipelineResources(pr.Namespace).Get, p, pr, ) @@ -216,7 +224,7 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er if rprt != nil { c.Logger.Infof("Creating a new TaskRun object %s", rprt.TaskRunName) - rprt.TaskRun, err = c.createTaskRun(c.Logger, pr.Namespace, rprt.ResolvedTaskResources.TaskName, rprt.TaskRunName, pr, rprt.PipelineTask, serviceAccount) + rprt.TaskRun, err = c.createTaskRun(c.Logger, rprt.ResolvedTaskResources.TaskName, rprt.TaskRunName, pr, rprt.PipelineTask, serviceAccount) if err != nil { c.Recorder.Eventf(pr, corev1.EventTypeWarning, "TaskRunCreationFailed", "Failed to create TaskRun %q: %v", rprt.TaskRunName, err) return fmt.Errorf("error creating TaskRun called %s for PipelineTask %s from PipelineRun %s: %s", rprt.TaskRunName, rprt.PipelineTask.Name, pr.Name, err) @@ -243,11 +251,11 @@ func UpdateTaskRunsStatus(pr *v1alpha1.PipelineRun, pipelineState []*resources.R } } -func (c *Reconciler) createTaskRun(logger *zap.SugaredLogger, namespace, taskName, trName string, pr *v1alpha1.PipelineRun, pt *v1alpha1.PipelineTask, sa string) (*v1alpha1.TaskRun, error) { +func (c *Reconciler) createTaskRun(logger *zap.SugaredLogger, taskName, taskRunName string, pr *v1alpha1.PipelineRun, pt *v1alpha1.PipelineTask, sa string) (*v1alpha1.TaskRun, error) { tr := &v1alpha1.TaskRun{ ObjectMeta: metav1.ObjectMeta{ - Name: trName, - Namespace: namespace, + Name: taskRunName, + Namespace: pr.Namespace, OwnerReferences: pr.GetOwnerReference(), Labels: map[string]string{ pipeline.GroupName + pipeline.PipelineLabelKey: pr.Spec.PipelineRef.Name, @@ -266,7 +274,7 @@ func (c *Reconciler) createTaskRun(logger *zap.SugaredLogger, namespace, taskNam } resources.WrapSteps(&tr.Spec, pr.Spec.PipelineTaskResources, pt) - return c.PipelineClientSet.PipelineV1alpha1().TaskRuns(namespace).Create(tr) + return c.PipelineClientSet.PipelineV1alpha1().TaskRuns(pr.Namespace).Create(tr) } func (c *Reconciler) updateStatus(pr *v1alpha1.PipelineRun) (*v1alpha1.PipelineRun, error) { diff --git a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun_test.go b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun_test.go index fe668ed43f5..535dd3cf62a 100644 --- a/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun_test.go +++ b/pkg/reconciler/v1alpha1/pipelinerun/pipelinerun_test.go @@ -71,6 +71,25 @@ func TestReconcile(t *testing.T) { Name: "some-repo", }, }}, + }, { + Name: "unit-test-cluster-task", + Inputs: []v1alpha1.TaskResourceBinding{{ + Name: "workspace", + ResourceRef: v1alpha1.PipelineResourceRef{ + Name: "some-repo", + }, + }}, + Outputs: []v1alpha1.TaskResourceBinding{{ + Name: "image-to-use", + ResourceRef: v1alpha1.PipelineResourceRef{ + Name: "some-image", + }, + }, { + Name: "workspace", + ResourceRef: v1alpha1.PipelineResourceRef{ + Name: "some-repo", + }, + }}, }}, ServiceAccount: "test-sa", }, @@ -101,6 +120,22 @@ func TestReconcile(t *testing.T) { Name: "workspace", ProvidedBy: []string{"unit-test-1"}, }}, + }, { + Name: "unit-test-cluster-task", + TaskRef: v1alpha1.TaskRef{ + Name: "unit-test-cluster-task", + Kind: "ClusterTask", + }, + Params: []v1alpha1.Param{{ + Name: "foo", + Value: "somethingfun", + }, { + Name: "bar", + Value: "somethingmorefun", + }, { + Name: "templatedparam", + Value: "${inputs.workspace.revision}", + }}, }}, }, }} @@ -147,6 +182,48 @@ func TestReconcile(t *testing.T) { }, }, }} + clusterTasks := []*v1alpha1.ClusterTask{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "unit-test-cluster-task", + }, + Spec: v1alpha1.TaskSpec{ + Inputs: &v1alpha1.Inputs{ + Resources: []v1alpha1.TaskResource{{ + Name: "workspace", + Type: "git", + }}, + Params: []v1alpha1.TaskParam{{ + Name: "foo", + Description: "foo", + }, { + Name: "bar", + Description: "bar", + }}, + }, + Outputs: &v1alpha1.Outputs{ + Resources: []v1alpha1.TaskResource{{ + Name: "image-to-use", + Type: "image", + }, { + Name: "workspace", + Type: "git", + }}, + }, + }, + }, { + ObjectMeta: metav1.ObjectMeta{ + Name: "unit-test-followup-task", + Namespace: "foo", + }, + Spec: v1alpha1.TaskSpec{ + Inputs: &v1alpha1.Inputs{ + Resources: []v1alpha1.TaskResource{{ + Name: "workspace", + Type: "git", + }}, + }, + }, + }} rs := []*v1alpha1.PipelineResource{{ ObjectMeta: metav1.ObjectMeta{ Name: "some-repo", @@ -176,6 +253,7 @@ func TestReconcile(t *testing.T) { PipelineRuns: prs, Pipelines: ps, Tasks: ts, + ClusterTasks: clusterTasks, PipelineResources: rs, } diff --git a/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution.go b/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution.go index e590bc41a6e..3fbc5a6db89 100644 --- a/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution.go +++ b/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution.go @@ -124,7 +124,7 @@ func getPipelineRunTaskResources(pipelineTaskName string, pr *v1alpha1.PipelineR // instances from getTask. If it is unable to retrieve an instance of a referenced Task, it // will return an error, otherwise it returns a list of all of the Tasks retrieved. // It will retrieve the Resources needed for the TaskRun as well using getResource. -func ResolvePipelineRun(getTask resources.GetTask, getResource resources.GetResource, p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) ([]*ResolvedPipelineRunTask, error) { +func ResolvePipelineRun(getTask resources.GetTask, getClusterTask resources.GetClusterTask, getResource resources.GetResource, p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) ([]*ResolvedPipelineRunTask, error) { state := []*ResolvedPipelineRunTask{} for i := range p.Spec.Tasks { pt := p.Spec.Tasks[i] @@ -135,7 +135,13 @@ func ResolvePipelineRun(getTask resources.GetTask, getResource resources.GetReso } // Find the Task that this task in the Pipeline is using - t, err := getTask(pt.TaskRef.Name) + var t v1alpha1.TaskInterface + var err error + if pt.TaskRef.Kind == v1alpha1.ClusterTaskKind { + t, err = getClusterTask(pt.TaskRef.Name) + } else { + t, err = getTask(pt.TaskRef.Name) + } if err != nil { // If the Task can't be found, it means the PipelineRun is invalid. Return the same error // type so it can be used by the caller. @@ -144,9 +150,10 @@ func ResolvePipelineRun(getTask resources.GetTask, getResource resources.GetReso // Get all the resources that this task will be using, if any inputs, outputs := getPipelineRunTaskResources(pt.Name, pr) - rtr, err := resources.ResolveTaskResources(&t.Spec, t.Name, inputs, outputs, getResource) + spec := t.TaskSpec() + rtr, err := resources.ResolveTaskResources(&spec, t.TaskMetadata().Name, inputs, outputs, getResource) if err != nil { - return nil, fmt.Errorf("couldn't resolve task resources for task %q: %v", t.Name, err) + return nil, fmt.Errorf("couldn't resolve task resources for task %q: %v", t.TaskMetadata().Name, err) } rprt.ResolvedTaskResources = rtr diff --git a/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution_test.go b/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution_test.go index 2a059f68840..a9afa7661f2 100644 --- a/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution_test.go +++ b/pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinerunresolution_test.go @@ -65,6 +65,17 @@ var task = &v1alpha1.Task{ }, } +var clustertask = &v1alpha1.ClusterTask{ + ObjectMeta: metav1.ObjectMeta{ + Name: "task", + }, + Spec: v1alpha1.TaskSpec{ + Steps: []corev1.Container{{ + Name: "step1", + }}, + }, +} + var trs = []v1alpha1.TaskRun{{ ObjectMeta: metav1.ObjectMeta{ Namespace: "namespace", @@ -287,10 +298,11 @@ func TestResolvePipelineRun(t *testing.T) { }, } - getTask := func(name string) (*v1alpha1.Task, error) { return task, nil } + getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } + getClusterTask := func(name string) (v1alpha1.TaskInterface, error) { return clustertask, nil } getResource := func(name string) (*v1alpha1.PipelineResource, error) { return r, nil } - pipelineState, err := ResolvePipelineRun(getTask, getResource, p, pr) + pipelineState, err := ResolvePipelineRun(getTask, getClusterTask, getResource, p, pr) if err != nil { t.Fatalf("Error getting tasks for fake pipeline %s: %s", p.ObjectMeta.Name, err) } @@ -336,12 +348,15 @@ func TestResolvePipelineRun_PipelineTaskHasNoResources(t *testing.T) { // We don't bind any Resources here }, } - getTask := func(name string) (*v1alpha1.Task, error) { + getTask := func(name string) (v1alpha1.TaskInterface, error) { return task, nil } + getClusterTask := func(name string) (v1alpha1.TaskInterface, error) { + return clustertask, nil + } getResource := func(name string) (*v1alpha1.PipelineResource, error) { return nil, fmt.Errorf("should not get called") } - pipelineState, err := ResolvePipelineRun(getTask, getResource, p, pr) + pipelineState, err := ResolvePipelineRun(getTask, getClusterTask, getResource, p, pr) if err != nil { t.Fatalf("Did not expect error when resolving PipelineRun without Resources: %v", err) } @@ -374,12 +389,15 @@ func TestResolvePipelineRun_TaskDoesntExist(t *testing.T) { }, } // Return an error when the Task is retrieved, as if it didn't exist - getTask := func(name string) (*v1alpha1.Task, error) { + getTask := func(name string) (v1alpha1.TaskInterface, error) { return nil, errors.NewNotFound(v1alpha1.Resource("task"), name) } + getClusterTask := func(name string) (v1alpha1.TaskInterface, error) { + return nil, errors.NewNotFound(v1alpha1.Resource("clustertask"), name) + } getResource := func(name string) (*v1alpha1.PipelineResource, error) { return nil, fmt.Errorf("should not get called") } - _, err := ResolvePipelineRun(getTask, getResource, p, pr) + _, err := ResolvePipelineRun(getTask, getClusterTask, getResource, p, pr) if err == nil { t.Fatalf("Expected error getting non-existent Tasks for Pipeline %s but got none", p.Name) } diff --git a/pkg/reconciler/v1alpha1/taskrun/resources/taskspec.go b/pkg/reconciler/v1alpha1/taskrun/resources/taskspec.go index 533868bba88..72080d06ad0 100644 --- a/pkg/reconciler/v1alpha1/taskrun/resources/taskspec.go +++ b/pkg/reconciler/v1alpha1/taskrun/resources/taskspec.go @@ -23,12 +23,15 @@ import ( ) // GetTask is a function used to retrieve Tasks. -type GetTask func(string) (*v1alpha1.Task, error) +type GetTask func(string) (v1alpha1.TaskInterface, error) + +// GetClusterTask is a function that will retrieve the Task from name and namespace. +type GetClusterTask func(name string) (v1alpha1.TaskInterface, error) // GetTaskSpec will retrieve the Task Spec associated with the provieded TaskRun. This can come from a // reference Task or from an embeded Task spec. func GetTaskSpec(taskRunSpec *v1alpha1.TaskRunSpec, taskRunName string, getTask GetTask) (*v1alpha1.TaskSpec, string, error) { - taskSpec := &v1alpha1.TaskSpec{} + taskSpec := v1alpha1.TaskSpec{} taskName := "" if taskRunSpec.TaskRef != nil && taskRunSpec.TaskRef.Name != "" { // Get related task for taskrun @@ -36,13 +39,13 @@ func GetTaskSpec(taskRunSpec *v1alpha1.TaskRunSpec, taskRunName string, getTask if err != nil { return nil, taskName, fmt.Errorf("error when listing tasks for taskRun %s %v", taskRunName, err) } - taskSpec = &t.Spec - taskName = t.Name + taskSpec = t.TaskSpec() + taskName = t.TaskMetadata().Name } else if taskRunSpec.TaskSpec != nil { - taskSpec = taskRunSpec.TaskSpec + taskSpec = *taskRunSpec.TaskSpec taskName = taskRunName } else { - return taskSpec, taskName, fmt.Errorf("TaskRun %s not providing TaskRef or TaskSpec", taskRunName) + return &taskSpec, taskName, fmt.Errorf("TaskRun %s not providing TaskRef or TaskSpec", taskRunName) } - return taskSpec, taskName, nil + return &taskSpec, taskName, nil } diff --git a/pkg/reconciler/v1alpha1/taskrun/resources/taskspec_test.go b/pkg/reconciler/v1alpha1/taskrun/resources/taskspec_test.go index f5efa879da5..72dbf759b6c 100644 --- a/pkg/reconciler/v1alpha1/taskrun/resources/taskspec_test.go +++ b/pkg/reconciler/v1alpha1/taskrun/resources/taskspec_test.go @@ -41,7 +41,7 @@ func TestGetTaskSpec_Ref(t *testing.T) { Name: "orchestrate", }, } - gt := func(n string) (*v1alpha1.Task, error) { return task, nil } + gt := func(n string) (v1alpha1.TaskInterface, error) { return task, nil } taskSpec, name, err := GetTaskSpec(spec, "mytaskrun", gt) if err != nil { @@ -64,7 +64,7 @@ func TestGetTaskSpec_Embedded(t *testing.T) { Name: "step1", }}, }} - gt := func(n string) (*v1alpha1.Task, error) { return nil, fmt.Errorf("shouldn't be called") } + gt := func(n string) (v1alpha1.TaskInterface, error) { return nil, fmt.Errorf("shouldn't be called") } taskSpec, name, err := GetTaskSpec(spec, "mytaskrun", gt) if err != nil { @@ -82,7 +82,7 @@ func TestGetTaskSpec_Embedded(t *testing.T) { func TestGetTaskSpec_Invalid(t *testing.T) { spec := &v1alpha1.TaskRunSpec{} - gt := func(n string) (*v1alpha1.Task, error) { return nil, fmt.Errorf("shouldn't be called") } + gt := func(n string) (v1alpha1.TaskInterface, error) { return nil, fmt.Errorf("shouldn't be called") } _, _, err := GetTaskSpec(spec, "mytaskrun", gt) if err == nil { t.Fatalf("Expected error resolving spec with no embedded or referenced task spec but didn't get error") @@ -95,7 +95,7 @@ func TestGetTaskSpec_Error(t *testing.T) { Name: "orchestrate", }, } - gt := func(n string) (*v1alpha1.Task, error) { return nil, fmt.Errorf("something went wrong") } + gt := func(n string) (v1alpha1.TaskInterface, error) { return nil, fmt.Errorf("something went wrong") } _, _, err := GetTaskSpec(spec, "mytaskrun", gt) if err == nil { t.Fatalf("Expected error when unable to find referenced Task but got none") diff --git a/pkg/reconciler/v1alpha1/taskrun/taskrun.go b/pkg/reconciler/v1alpha1/taskrun/taskrun.go index bc5be3f5262..81e3e5c883a 100644 --- a/pkg/reconciler/v1alpha1/taskrun/taskrun.go +++ b/pkg/reconciler/v1alpha1/taskrun/taskrun.go @@ -90,11 +90,12 @@ type Reconciler struct { *reconciler.Base // listers index properties about resources - taskRunLister listers.TaskRunLister - taskLister listers.TaskLister - resourceLister listers.PipelineResourceLister - tracker tracker.Interface - configStore configStore + taskRunLister listers.TaskRunLister + taskLister listers.TaskLister + clusterTaskLister listers.ClusterTaskLister + resourceLister listers.PipelineResourceLister + tracker tracker.Interface + configStore configStore } // Check that our Reconciler implements controller.Reconciler @@ -105,15 +106,17 @@ func NewController( opt reconciler.Options, taskRunInformer informers.TaskRunInformer, taskInformer informers.TaskInformer, + clusterTaskInformer informers.ClusterTaskInformer, buildInformer buildinformers.BuildInformer, resourceInformer informers.PipelineResourceInformer, ) *controller.Impl { c := &Reconciler{ - Base: reconciler.NewBase(opt, taskRunAgentName), - taskRunLister: taskRunInformer.Lister(), - taskLister: taskInformer.Lister(), - resourceLister: resourceInformer.Lister(), + Base: reconciler.NewBase(opt, taskRunAgentName), + taskRunLister: taskRunInformer.Lister(), + taskLister: taskInformer.Lister(), + clusterTaskLister: clusterTaskInformer.Lister(), + resourceLister: resourceInformer.Lister(), } impl := controller.NewImpl(c, c.Logger, taskRunControllerName, reconciler.MustNewStatsReporter(taskRunControllerName, c.Logger)) @@ -181,8 +184,31 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error { return err } +func (c *Reconciler) getTaskFunc(tr *v1alpha1.TaskRun) resources.GetTask { + var gtFunc resources.GetTask + if tr.Spec.TaskRef != nil && tr.Spec.TaskRef.Kind == v1alpha1.ClusterTaskKind { + gtFunc = func(name string) (v1alpha1.TaskInterface, error) { + t, err := c.clusterTaskLister.Get(name) + if err != nil { + return nil, err + } + return t, nil + } + } else { + gtFunc = func(name string) (v1alpha1.TaskInterface, error) { + t, err := c.taskLister.Tasks(tr.Namespace).Get(name) + if err != nil { + return nil, err + } + return t, nil + } + } + return gtFunc +} + func (c *Reconciler) reconcile(ctx context.Context, tr *v1alpha1.TaskRun) error { - spec, taskName, err := resources.GetTaskSpec(&tr.Spec, tr.Name, c.taskLister.Tasks(tr.Namespace).Get) + getTaskFunc := c.getTaskFunc(tr) + spec, taskName, err := resources.GetTaskSpec(&tr.Spec, tr.Name, getTaskFunc) if err != nil { c.Logger.Error("Failed to determine Task spec to use for taskrun %s: %v", tr.Name, err) tr.Status.SetCondition(&duckv1alpha1.Condition{ diff --git a/test/controller.go b/test/controller.go index 5dcacb18a75..e08c4e503c8 100644 --- a/test/controller.go +++ b/test/controller.go @@ -51,6 +51,7 @@ type Data struct { Pipelines []*v1alpha1.Pipeline TaskRuns []*v1alpha1.TaskRun Tasks []*v1alpha1.Task + ClusterTasks []*v1alpha1.ClusterTask PipelineResources []*v1alpha1.PipelineResource Builds []*buildv1alpha1.Build } @@ -68,6 +69,7 @@ type Informers struct { Pipeline informersv1alpha1.PipelineInformer TaskRun informersv1alpha1.TaskRunInformer Task informersv1alpha1.TaskInformer + ClusterTask informersv1alpha1.ClusterTaskInformer PipelineResource informersv1alpha1.PipelineResourceInformer Build buildinformersv1alpha1.BuildInformer } @@ -86,6 +88,9 @@ func seedTestData(d Data) (Clients, Informers) { for _, t := range d.Tasks { objs = append(objs, t) } + for _, ct := range d.ClusterTasks { + objs = append(objs, ct) + } for _, tr := range d.TaskRuns { objs = append(objs, tr) } @@ -107,6 +112,7 @@ func seedTestData(d Data) (Clients, Informers) { Pipeline: sharedInformer.Pipeline().V1alpha1().Pipelines(), TaskRun: sharedInformer.Pipeline().V1alpha1().TaskRuns(), Task: sharedInformer.Pipeline().V1alpha1().Tasks(), + ClusterTask: sharedInformer.Pipeline().V1alpha1().ClusterTasks(), PipelineResource: sharedInformer.Pipeline().V1alpha1().PipelineResources(), Build: buildInformerFactory.Build().V1alpha1().Builds(), } @@ -123,6 +129,9 @@ func seedTestData(d Data) (Clients, Informers) { for _, t := range d.Tasks { i.Task.Informer().GetIndexer().Add(t) } + for _, ct := range d.ClusterTasks { + i.ClusterTask.Informer().GetIndexer().Add(ct) + } for _, r := range d.PipelineResources { i.PipelineResource.Informer().GetIndexer().Add(r) } @@ -148,6 +157,7 @@ func GetTaskRunController(d Data) (*controller.Impl, *observer.ObservedLogs, Cli }, i.TaskRun, i.Task, + i.ClusterTask, i.Build, i.PipelineResource, ), logs, c @@ -167,6 +177,7 @@ func GetPipelineRunController(d Data) (*controller.Impl, *observer.ObservedLogs, i.PipelineRun, i.Pipeline, i.Task, + i.ClusterTask, i.TaskRun, i.PipelineResource, ), logs, c