From af4b716da0ef782d497cbadc04e23436cdbc5068 Mon Sep 17 00:00:00 2001 From: Jason Hall Date: Sat, 27 Jun 2020 00:03:02 -0400 Subject: [PATCH] Define v1alpha1 Run type and generated scaffolding This defines the basic Run type, which will be used to specify executions of Custom Tasks and report status of those executions. This change doesn't wire the new type in to any controllers or validating webhooks, so that this time nothing will happen when a Run is created. It also doesn't wire in to PipelineRun execution to enable Pipelines to specify Custom Tasks. --- config/300-run.yaml | 71 ++++ go.mod | 5 - go.sum | 7 +- pkg/apis/pipeline/controller.go | 5 +- pkg/apis/pipeline/register.go | 8 + pkg/apis/pipeline/v1alpha1/register.go | 2 + pkg/apis/pipeline/v1alpha1/run_defaults.go | 34 ++ pkg/apis/pipeline/v1alpha1/run_types.go | 197 ++++++++++ pkg/apis/pipeline/v1alpha1/run_validation.go | 54 +++ .../pipeline/v1alpha1/run_validation_test.go | 100 +++++ .../v1alpha1/zz_generated.deepcopy.go | 143 +++++++ .../v1alpha1/fake/fake_pipeline_client.go | 4 + .../typed/pipeline/v1alpha1/fake/fake_run.go | 140 +++++++ .../pipeline/v1alpha1/generated_expansion.go | 2 + .../pipeline/v1alpha1/pipeline_client.go | 5 + .../versioned/typed/pipeline/v1alpha1/run.go | 191 ++++++++++ .../informers/externalversions/generic.go | 2 + .../pipeline/v1alpha1/interface.go | 7 + .../externalversions/pipeline/v1alpha1/run.go | 89 +++++ .../pipeline/v1alpha1/run/fake/fake.go | 40 ++ .../informers/pipeline/v1alpha1/run/run.go | 52 +++ .../pipeline/v1alpha1/run/controller.go | 118 ++++++ .../pipeline/v1alpha1/run/reconciler.go | 352 ++++++++++++++++++ .../pipeline/v1alpha1/run/stub/controller.go | 54 +++ .../pipeline/v1alpha1/run/stub/reconciler.go | 66 ++++ .../pipeline/v1alpha1/expansion_generated.go | 8 + pkg/client/listers/pipeline/v1alpha1/run.go | 94 +++++ 27 files changed, 1841 insertions(+), 9 deletions(-) create mode 100644 config/300-run.yaml create mode 100644 pkg/apis/pipeline/v1alpha1/run_defaults.go create mode 100644 pkg/apis/pipeline/v1alpha1/run_types.go create mode 100644 pkg/apis/pipeline/v1alpha1/run_validation.go create mode 100644 pkg/apis/pipeline/v1alpha1/run_validation_test.go create mode 100644 pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_run.go create mode 100644 pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go create mode 100644 pkg/client/informers/externalversions/pipeline/v1alpha1/run.go create mode 100644 pkg/client/injection/informers/pipeline/v1alpha1/run/fake/fake.go create mode 100644 pkg/client/injection/informers/pipeline/v1alpha1/run/run.go create mode 100644 pkg/client/injection/reconciler/pipeline/v1alpha1/run/controller.go create mode 100644 pkg/client/injection/reconciler/pipeline/v1alpha1/run/reconciler.go create mode 100644 pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/controller.go create mode 100644 pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/reconciler.go create mode 100644 pkg/client/listers/pipeline/v1alpha1/run.go diff --git a/config/300-run.yaml b/config/300-run.yaml new file mode 100644 index 00000000000..b699c13b646 --- /dev/null +++ b/config/300-run.yaml @@ -0,0 +1,71 @@ +# Copyright 2019 The Tekton 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: runs.tekton.dev + labels: + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-pipelines + pipeline.tekton.dev/release: "devel" + version: "devel" +spec: + group: tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + names: + kind: Run + plural: runs + categories: + - tekton + - tekton-pipelines + scope: Namespaced + additionalPrinterColumns: + - name: Succeeded + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].status" + - name: Reason + type: string + JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].reason" + - name: StartTime + type: date + JSONPath: .status.startTime + - name: CompletionTime + type: date + JSONPath: .status.completionTime + # Opt into the status subresource so metadata.generation + # starts to increment + subresources: + status: {} + conversion: + strategy: Webhook + webhookClientConfig: + service: + name: tekton-pipelines-webhook + namespace: tekton-pipelines diff --git a/go.mod b/go.mod index 7b6fd11fb71..5230cf27725 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,6 @@ module github.com/tektoncd/pipeline go 1.13 require ( - cloud.google.com/go/storage v1.6.0 - contrib.go.opencensus.io/exporter/ocagent v0.6.0 // indirect - contrib.go.opencensus.io/exporter/prometheus v0.1.0 // indirect contrib.go.opencensus.io/exporter/stackdriver v0.13.1 // indirect github.com/GoogleCloudPlatform/cloud-builders/gcs-fetcher v0.0.0-20191203181535-308b93ad1f39 github.com/aws/aws-sdk-go v1.30.16 // indirect @@ -28,8 +25,6 @@ require ( golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d gomodules.xyz/jsonpatch/v2 v2.1.0 - google.golang.org/api v0.20.0 - google.golang.org/appengine v1.6.5 // indirect google.golang.org/protobuf v1.22.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect k8s.io/api v0.17.6 diff --git a/go.sum b/go.sum index 19a2ee3e5bb..d08b260b63d 100644 --- a/go.sum +++ b/go.sum @@ -1271,9 +1271,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -1294,6 +1296,7 @@ gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.1/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= @@ -1307,6 +1310,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= helm.sh/helm/v3 v3.1.1/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1321,14 +1325,12 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 k8s.io/api v0.16.5 h1:5ceVzCX+I9LI9wIw4CASqVsepRDeUp44/J8Tgl4ki2I= k8s.io/api v0.16.5/go.mod h1:6ijJb2BQAkGSn+8Z3173M0LXe2gBAH+i/JAJptd/rJo= k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs= -k8s.io/apiextensions-apiserver v0.17.3/go.mod h1:CJbCyMfkKftAd/X/V6OTHYhVn7zXnDdnkUjS1h0GTeY= k8s.io/apiextensions-apiserver v0.17.6 h1:o5JWDya65ApIVez+RfR40PGrqjPUZHhlSmwAHCvL20E= k8s.io/apiextensions-apiserver v0.17.6/go.mod h1:Z3CHLP3Tha+Rbav7JR3S+ye427UaJkHBomK2c4XtZ3A= k8s.io/apimachinery v0.16.5 h1:5rnJuabGmLtgcMrdeTb48CvV0UW4TrM5vBbWWEy6OS0= k8s.io/apimachinery v0.16.5/go.mod h1:mhhO3hoLkWO+2eCvqjPtH2Ly92l9nJDwsswzWKpkN2w= k8s.io/apiserver v0.17.0/go.mod h1:ABM+9x/prjINN6iiffRVNCBR2Wk7uY4z+EtEGZD48cg= k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo= -k8s.io/apiserver v0.17.3/go.mod h1:iJtsPpu1ZpEnHaNawpSV0nYTGBhhX2dUlnn7/QS7QiY= k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I= k8s.io/apiserver v0.17.6/go.mod h1:sAYqm8hUDNA9aj/TzqwsJoExWrxprKv0tqs/z88qym0= k8s.io/cli-runtime v0.17.2/go.mod h1:aa8t9ziyQdbkuizkNLAw3qe3srSyWh9zlSB7zTqRNPI= @@ -1341,7 +1343,6 @@ k8s.io/code-generator v0.16.5 h1:DQDucFIbyQChnbz5KY0SECZd2VH72qKVAd8+vA4h8kI= k8s.io/code-generator v0.16.5/go.mod h1:2aiDuxDU7RQK2PVypXAXHo6+YwOlF33iezHQbSmKSA4= k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc= k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs= -k8s.io/component-base v0.17.3/go.mod h1:GeQf4BrgelWm64PXkIXiPh/XS0hnO42d9gx9BtbZRp8= k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE= k8s.io/component-base v0.17.6 h1:4S4FTX7/5VvO325vHm9/4pdql91OhrZpDYVzmyLSqNU= k8s.io/component-base v0.17.6/go.mod h1:jgRLWl0B0rOzFNtxQ9E4BphPmDqoMafujdau6AdG2Xo= diff --git a/pkg/apis/pipeline/controller.go b/pkg/apis/pipeline/controller.go index 38fe60b4be9..d49779ad1ae 100644 --- a/pkg/apis/pipeline/controller.go +++ b/pkg/apis/pipeline/controller.go @@ -21,6 +21,9 @@ const ( // nolint: golint PipelineRunControllerName = "PipelineRun" - // TaskRunControllerName holds the name of the PipelineRun controller + // TaskRunControllerName holds the name of the TaskRun controller TaskRunControllerName = "TaskRun" + + // TaskRunControllerName holds the name of the PipelineRun controller + RunControllerName = "TaskRun" ) diff --git a/pkg/apis/pipeline/register.go b/pkg/apis/pipeline/register.go index c65484d9763..f34e682c67a 100644 --- a/pkg/apis/pipeline/register.go +++ b/pkg/apis/pipeline/register.go @@ -45,6 +45,9 @@ const ( // ConditionNameKey is used as the label identifier for a Condition ConditionNameKey = "/conditionName" + + // RunKey is used as the label identifier for a Run + RunKey = "/run" ) var ( @@ -63,6 +66,11 @@ var ( Group: GroupName, Resource: "taskruns", } + // RunResource represents a Tekton Run + RunResource = schema.GroupResource{ + Group: GroupName, + Resource: "runs", + } // PipelineResource represents a Tekton Pipeline PipelineResource = schema.GroupResource{ Group: GroupName, diff --git a/pkg/apis/pipeline/v1alpha1/register.go b/pkg/apis/pipeline/v1alpha1/register.go index a8d52721405..78b59bfdce1 100644 --- a/pkg/apis/pipeline/v1alpha1/register.go +++ b/pkg/apis/pipeline/v1alpha1/register.go @@ -60,6 +60,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &PipelineRunList{}, &PipelineResource{}, &PipelineResourceList{}, + &Run{}, + &RunList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/pkg/apis/pipeline/v1alpha1/run_defaults.go b/pkg/apis/pipeline/v1alpha1/run_defaults.go new file mode 100644 index 00000000000..77d6d7861f6 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/run_defaults.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Tekton 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 ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Run)(nil) + +func (r *Run) SetDefaults(ctx context.Context) { + ctx = apis.WithinParent(ctx, r.ObjectMeta) + r.Spec.SetDefaults(apis.WithinSpec(ctx)) +} + +func (rs *RunSpec) SetDefaults(ctx context.Context) { + // No defaults to set. +} diff --git a/pkg/apis/pipeline/v1alpha1/run_types.go b/pkg/apis/pipeline/v1alpha1/run_types.go new file mode 100644 index 00000000000..3bf86a9d8ca --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/run_types.go @@ -0,0 +1,197 @@ +/* +Copyright 2019 The Tekton 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 ( + "encoding/json" + "fmt" + + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/apis" + duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1" +) + +var ( + runGroupVersionKind = schema.GroupVersionKind{ + Group: SchemeGroupVersion.Group, + Version: SchemeGroupVersion.Version, + Kind: pipeline.RunControllerName, + } +) + +// RunSpec defines the desired state of Run +type RunSpec struct { + // +optional + Ref *TaskRef `json:"ref,omitempty"` + + // +optional + Params []v1beta1.Param `json:"params,omitempty"` + + // TODO(https://github.com/tektoncd/community/pull/128) + // - cancellation + // - timeout + // - inline task spec + // - workspaces ? +} + +// TODO: Move this to a Params type so other code can use it? +func (rs RunSpec) GetParam(name string) *v1beta1.Param { + for _, p := range rs.Params { + if p.Name == name { + return &p + } + } + return nil +} + +type RunStatus struct { + duckv1beta1.Status `json:",inline"` + + // RunStatusFields inlines the status fields. + RunStatusFields `json:",inline"` +} + +// RunStatusFields holds the fields of Run's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type RunStatusFields struct { + // AdditionalFields holds arbitrary data reported by a controller. + AdditionalFields json.RawMessage `json:"additionalFields,inline"` + + // StartTime is the time the build is actually started. + // +optional + StartTime *metav1.Time `json:"startTime,omitempty"` + + // CompletionTime is the time the build completed. + // +optional + CompletionTime *metav1.Time `json:"completionTime,omitempty"` + + // Results reports any output result values to be consumed by later + // tasks in a pipeline. + // +optional + Results []v1beta1.TaskRunResult `json:"results,omitempty"` +} + +// Get returns the additional field value with the given key. +func (s *RunStatusFields) GetAdditionalField(k string) (interface{}, error) { + var ad interface{} + if len(s.AdditionalFields) == 0 { + ad = map[string]interface{}{} + } else { + if err := json.Unmarshal(s.AdditionalFields, &ad); err != nil { + return nil, err + } + } + adm, ok := ad.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("AdditionalFields was not a map[string]interface{}, got %T", ad) + } + return adm[k], nil +} + +// Set sets a new additional field with the given value. +func (s *RunStatusFields) SetAdditionalField(k string, d interface{}) error { + var ad interface{} + if len(s.AdditionalFields) == 0 { + ad = map[string]interface{}{} + } else { + if err := json.Unmarshal(s.AdditionalFields, &ad); err != nil { + return err + } + } + adm, ok := ad.(map[string]interface{}) + if !ok { + return fmt.Errorf("AdditionalFields was not a map[string]interface{}, got %T", ad) + } + adm[k] = d + b, err := json.Marshal(adm) + if err != nil { + return err + } + s.AdditionalFields = b + return nil +} + +// TODO: clear key + +// +genclient +// +genreconciler +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Run represents a single execution of a Custom Task. +// +// +k8s:openapi-gen=true +type Run struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec RunSpec `json:"spec,omitempty"` + // +optional + Status RunStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RunList contains a list of Run +type RunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Run `json:"items"` +} + +// GetOwnerReference gets the task run as owner reference for any related objects +func (r *Run) GetOwnerReference() metav1.OwnerReference { + return *metav1.NewControllerRef(r, runGroupVersionKind) +} + +// HasPipelineRunOwnerReference returns true of Run has +// owner reference of type PipelineRun +func (r *Run) HasPipelineRunOwnerReference() bool { + for _, ref := range r.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return true + } + } + return false +} + +// IsDone returns true if the Run's status indicates that it is done. +func (r *Run) IsDone() bool { + return !r.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether taskrun has valid start time set in its status +func (r *Run) HasStarted() bool { + return r.Status.StartTime != nil && !r.Status.StartTime.IsZero() +} + +// IsSuccessful returns true if the Run's status indicates that it is done. +func (r *Run) IsSuccessful() bool { + return r.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// GetRunKey return the taskrun key for timeout handler map +func (r *Run) GetRunKey() string { + // The address of the pointer is a threadsafe unique identifier for the taskrun + return fmt.Sprintf("%s/%p", "Run", r) +} diff --git a/pkg/apis/pipeline/v1alpha1/run_validation.go b/pkg/apis/pipeline/v1alpha1/run_validation.go new file mode 100644 index 00000000000..ee332fba699 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/run_validation.go @@ -0,0 +1,54 @@ +/* +Copyright 2019 The Tekton 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 ( + "context" + + "github.com/tektoncd/pipeline/pkg/apis/validate" + "k8s.io/apimachinery/pkg/api/equality" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Run)(nil) + +// Validate taskrun +func (r *Run) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(r.GetObjectMeta()).ViaField("metadata"); err != nil { + return err + } + return r.Spec.Validate(ctx) +} + +// Validate Run spec +func (rs *RunSpec) Validate(ctx context.Context) *apis.FieldError { + if equality.Semantic.DeepEqual(rs, &RunSpec{}) { + return apis.ErrMissingField("spec") + } + + if rs.Ref == nil { + return apis.ErrMissingField("spec.ref") + } + if rs.Ref.APIVersion == "" { + return apis.ErrMissingField("spec.ref.apiVersion") + } + if rs.Ref.Kind == "" { + return apis.ErrMissingField("spec.ref.kind") + } + + return nil +} diff --git a/pkg/apis/pipeline/v1alpha1/run_validation_test.go b/pkg/apis/pipeline/v1alpha1/run_validation_test.go new file mode 100644 index 00000000000..0eb8085d985 --- /dev/null +++ b/pkg/apis/pipeline/v1alpha1/run_validation_test.go @@ -0,0 +1,100 @@ +/* +Copyright 2019 The Tekton 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_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/test/diff" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +func TestRun_Invalid(t *testing.T) { + for _, c := range []struct { + name string + run *v1alpha1.Run + want *apis.FieldError + }{{ + name: "missing spec", + run: &v1alpha1.Run{}, + want: apis.ErrMissingField("spec"), + }, { + name: "invalid metadata", + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{Name: "run.name"}, + }, + want: &apis.FieldError{ + Message: "Invalid resource name: special character . must not be present", + Paths: []string{"metadata.name"}, + }, + }, { + name: "missing ref", + run: &v1alpha1.Run{ + Spec: v1alpha1.RunSpec{ + Ref: nil, + }, + }, + want: apis.ErrMissingField("spec"), + }, { + name: "missing apiVersion", + run: &v1alpha1.Run{ + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: "", + }, + }, + }, + want: apis.ErrMissingField("spec.ref.apiVersion"), + }, { + name: "missing kind", + run: &v1alpha1.Run{ + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: "blah", + Kind: "", + }, + }, + }, + want: apis.ErrMissingField("spec.ref.kind"), + }} { + t.Run(c.name, func(t *testing.T) { + err := c.run.Validate(context.Background()) + if d := cmp.Diff(err.Error(), c.want.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} + +func TestRun_Valid(t *testing.T) { + r := &v1alpha1.Run{ + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: "blah", + Kind: "blah", + Name: "blah", + }, + }, + } + if err := r.Validate(context.Background()); err != nil { + t.Fatalf("validating valid Run: %v", err) + } +} diff --git a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go index 55e61fb5d07..bc3aee0206d 100644 --- a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,8 @@ limitations under the License. package v1alpha1 import ( + json "encoding/json" + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" resourcev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" @@ -610,6 +612,147 @@ func (in *PipelineTaskRunSpec) DeepCopy() *PipelineTaskRunSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Run) DeepCopyInto(out *Run) { + *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 Run. +func (in *Run) DeepCopy() *Run { + if in == nil { + return nil + } + out := new(Run) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Run) 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 *RunList) DeepCopyInto(out *RunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Run, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunList. +func (in *RunList) DeepCopy() *RunList { + if in == nil { + return nil + } + out := new(RunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RunList) 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 *RunSpec) DeepCopyInto(out *RunSpec) { + *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + *out = new(v1beta1.TaskRef) + **out = **in + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]v1beta1.Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunSpec. +func (in *RunSpec) DeepCopy() *RunSpec { + if in == nil { + return nil + } + out := new(RunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunStatus) DeepCopyInto(out *RunStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.RunStatusFields.DeepCopyInto(&out.RunStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunStatus. +func (in *RunStatus) DeepCopy() *RunStatus { + if in == nil { + return nil + } + out := new(RunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RunStatusFields) DeepCopyInto(out *RunStatusFields) { + *out = *in + if in.AdditionalFields != nil { + in, out := &in.AdditionalFields, &out.AdditionalFields + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) + } + if in.StartTime != nil { + in, out := &in.StartTime, &out.StartTime + *out = (*in).DeepCopy() + } + if in.CompletionTime != nil { + in, out := &in.CompletionTime, &out.CompletionTime + *out = (*in).DeepCopy() + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]v1beta1.TaskRunResult, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RunStatusFields. +func (in *RunStatusFields) DeepCopy() *RunStatusFields { + if in == nil { + return nil + } + out := new(RunStatusFields) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Task) DeepCopyInto(out *Task) { *out = *in 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 5bcbcb1cd5a..f04cb586314 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 @@ -44,6 +44,10 @@ func (c *FakeTektonV1alpha1) PipelineRuns(namespace string) v1alpha1.PipelineRun return &FakePipelineRuns{c, namespace} } +func (c *FakeTektonV1alpha1) Runs(namespace string) v1alpha1.RunInterface { + return &FakeRuns{c, namespace} +} + func (c *FakeTektonV1alpha1) Tasks(namespace string) v1alpha1.TaskInterface { return &FakeTasks{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_run.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_run.go new file mode 100644 index 00000000000..354630d2a96 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/fake/fake_run.go @@ -0,0 +1,140 @@ +/* +Copyright 2020 The Tekton 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 ( + v1alpha1 "github.com/tektoncd/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" +) + +// FakeRuns implements RunInterface +type FakeRuns struct { + Fake *FakeTektonV1alpha1 + ns string +} + +var runsResource = schema.GroupVersionResource{Group: "tekton.dev", Version: "v1alpha1", Resource: "runs"} + +var runsKind = schema.GroupVersionKind{Group: "tekton.dev", Version: "v1alpha1", Kind: "Run"} + +// Get takes name of the run, and returns the corresponding run object, and an error if there is any. +func (c *FakeRuns) Get(name string, options v1.GetOptions) (result *v1alpha1.Run, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(runsResource, c.ns, name), &v1alpha1.Run{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Run), err +} + +// List takes label and field selectors, and returns the list of Runs that match those selectors. +func (c *FakeRuns) List(opts v1.ListOptions) (result *v1alpha1.RunList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(runsResource, runsKind, c.ns, opts), &v1alpha1.RunList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.RunList{ListMeta: obj.(*v1alpha1.RunList).ListMeta} + for _, item := range obj.(*v1alpha1.RunList).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 runs. +func (c *FakeRuns) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(runsResource, c.ns, opts)) + +} + +// Create takes the representation of a run and creates it. Returns the server's representation of the run, and an error, if there is any. +func (c *FakeRuns) Create(run *v1alpha1.Run) (result *v1alpha1.Run, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(runsResource, c.ns, run), &v1alpha1.Run{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Run), err +} + +// Update takes the representation of a run and updates it. Returns the server's representation of the run, and an error, if there is any. +func (c *FakeRuns) Update(run *v1alpha1.Run) (result *v1alpha1.Run, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(runsResource, c.ns, run), &v1alpha1.Run{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Run), 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 *FakeRuns) UpdateStatus(run *v1alpha1.Run) (*v1alpha1.Run, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(runsResource, "status", c.ns, run), &v1alpha1.Run{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Run), err +} + +// Delete takes name of the run and deletes it. Returns an error if one occurs. +func (c *FakeRuns) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(runsResource, c.ns, name), &v1alpha1.Run{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeRuns) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(runsResource, c.ns, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.RunList{}) + return err +} + +// Patch applies the patch and returns the patched run. +func (c *FakeRuns) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Run, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(runsResource, c.ns, name, pt, data, subresources...), &v1alpha1.Run{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Run), err +} 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 a3b6b03b044..6942df0f45e 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/generated_expansion.go @@ -26,6 +26,8 @@ type PipelineExpansion interface{} type PipelineRunExpansion interface{} +type RunExpansion interface{} + type TaskExpansion interface{} type TaskRunExpansion 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 f96f3e34db9..5797a3003a7 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/pipeline_client.go @@ -30,6 +30,7 @@ type TektonV1alpha1Interface interface { ConditionsGetter PipelinesGetter PipelineRunsGetter + RunsGetter TasksGetter TaskRunsGetter } @@ -55,6 +56,10 @@ func (c *TektonV1alpha1Client) PipelineRuns(namespace string) PipelineRunInterfa return newPipelineRuns(c, namespace) } +func (c *TektonV1alpha1Client) Runs(namespace string) RunInterface { + return newRuns(c, namespace) +} + func (c *TektonV1alpha1Client) Tasks(namespace string) TaskInterface { return newTasks(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go new file mode 100644 index 00000000000..9efaff0baad --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1alpha1/run.go @@ -0,0 +1,191 @@ +/* +Copyright 2020 The Tekton 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 v1alpha1 + +import ( + "time" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + scheme "github.com/tektoncd/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" +) + +// RunsGetter has a method to return a RunInterface. +// A group's client should implement this interface. +type RunsGetter interface { + Runs(namespace string) RunInterface +} + +// RunInterface has methods to work with Run resources. +type RunInterface interface { + Create(*v1alpha1.Run) (*v1alpha1.Run, error) + Update(*v1alpha1.Run) (*v1alpha1.Run, error) + UpdateStatus(*v1alpha1.Run) (*v1alpha1.Run, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.Run, error) + List(opts v1.ListOptions) (*v1alpha1.RunList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Run, err error) + RunExpansion +} + +// runs implements RunInterface +type runs struct { + client rest.Interface + ns string +} + +// newRuns returns a Runs +func newRuns(c *TektonV1alpha1Client, namespace string) *runs { + return &runs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the run, and returns the corresponding run object, and an error if there is any. +func (c *runs) Get(name string, options v1.GetOptions) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Get(). + Namespace(c.ns). + Resource("runs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Runs that match those selectors. +func (c *runs) List(opts v1.ListOptions) (result *v1alpha1.RunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.RunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested runs. +func (c *runs) Watch(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(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch() +} + +// Create takes the representation of a run and creates it. Returns the server's representation of the run, and an error, if there is any. +func (c *runs) Create(run *v1alpha1.Run) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Post(). + Namespace(c.ns). + Resource("runs"). + Body(run). + Do(). + Into(result) + return +} + +// Update takes the representation of a run and updates it. Returns the server's representation of the run, and an error, if there is any. +func (c *runs) Update(run *v1alpha1.Run) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Put(). + Namespace(c.ns). + Resource("runs"). + Name(run.Name). + Body(run). + Do(). + 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 *runs) UpdateStatus(run *v1alpha1.Run) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Put(). + Namespace(c.ns). + Resource("runs"). + Name(run.Name). + SubResource("status"). + Body(run). + Do(). + Into(result) + return +} + +// Delete takes name of the run and deletes it. Returns an error if one occurs. +func (c *runs) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("runs"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *runs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + var timeout time.Duration + if listOptions.TimeoutSeconds != nil { + timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("runs"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Timeout(timeout). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched run. +func (c *runs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Run, err error) { + result = &v1alpha1.Run{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("runs"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index b7c6452058a..3d05774d1c5 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -62,6 +62,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Pipelines().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("pipelineruns"): return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().PipelineRuns().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("runs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Runs().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("tasks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1alpha1().Tasks().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("taskruns"): diff --git a/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go b/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go index fa069ae3945..5b9882c30d7 100644 --- a/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/pipeline/v1alpha1/interface.go @@ -32,6 +32,8 @@ type Interface interface { Pipelines() PipelineInformer // PipelineRuns returns a PipelineRunInformer. PipelineRuns() PipelineRunInformer + // Runs returns a RunInformer. + Runs() RunInformer // Tasks returns a TaskInformer. Tasks() TaskInformer // TaskRuns returns a TaskRunInformer. @@ -69,6 +71,11 @@ func (v *version) PipelineRuns() PipelineRunInformer { return &pipelineRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// Runs returns a RunInformer. +func (v *version) Runs() RunInformer { + return &runInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // Tasks returns a TaskInformer. func (v *version) Tasks() TaskInformer { return &taskInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go b/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go new file mode 100644 index 00000000000..3fa4984b1b6 --- /dev/null +++ b/pkg/client/informers/externalversions/pipeline/v1alpha1/run.go @@ -0,0 +1,89 @@ +/* +Copyright 2020 The Tekton 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 v1alpha1 + +import ( + time "time" + + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/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" +) + +// RunInformer provides access to a shared informer and lister for +// Runs. +type RunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.RunLister +} + +type runInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewRunInformer constructs a new informer for Run 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 NewRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredRunInformer constructs a new informer for Run 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 NewFilteredRunInformer(client versioned.Interface, namespace string, 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.TektonV1alpha1().Runs(namespace).List(options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1alpha1().Runs(namespace).Watch(options) + }, + }, + &pipelinev1alpha1.Run{}, + resyncPeriod, + indexers, + ) +} + +func (f *runInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *runInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1alpha1.Run{}, f.defaultInformer) +} + +func (f *runInformer) Lister() v1alpha1.RunLister { + return v1alpha1.NewRunLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/injection/informers/pipeline/v1alpha1/run/fake/fake.go b/pkg/client/injection/informers/pipeline/v1alpha1/run/fake/fake.go new file mode 100644 index 00000000000..14d4c6fa1d4 --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1alpha1/run/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "github.com/tektoncd/pipeline/pkg/client/injection/informers/factory/fake" + run "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = run.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Tekton().V1alpha1().Runs() + return context.WithValue(ctx, run.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/injection/informers/pipeline/v1alpha1/run/run.go b/pkg/client/injection/informers/pipeline/v1alpha1/run/run.go new file mode 100644 index 00000000000..fc3b89bb9c4 --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1alpha1/run/run.go @@ -0,0 +1,52 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package run + +import ( + context "context" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1" + factory "github.com/tektoncd/pipeline/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Tekton().V1alpha1().Runs() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.RunInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1.RunInformer from context.") + } + return untyped.(v1alpha1.RunInformer) +} diff --git a/pkg/client/injection/reconciler/pipeline/v1alpha1/run/controller.go b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/controller.go new file mode 100644 index 00000000000..bbae77bbba6 --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/controller.go @@ -0,0 +1,118 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package run + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + versionedscheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + client "github.com/tektoncd/pipeline/pkg/client/injection/client" + run "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run" + corev1 "k8s.io/api/core/v1" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + kubeclient "knative.dev/pkg/client/injection/kube/client" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" +) + +const ( + defaultControllerAgentName = "run-controller" + defaultFinalizerName = "runs.tekton.dev" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.Options to be used but the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(ctx) + + // Check the options function input. It should be 0 or 1. + if len(optionsFns) > 1 { + logger.Fatalf("up to one options function is supported, found %d", len(optionsFns)) + } + + runInformer := run.Get(ctx) + + rec := &reconcilerImpl{ + Client: client.Get(ctx), + Lister: runInformer.Lister(), + reconciler: r, + finalizerName: defaultFinalizerName, + } + + t := reflect.TypeOf(r).Elem() + queueName := fmt.Sprintf("%s.%s", strings.ReplaceAll(t.PkgPath(), "/", "-"), t.Name()) + + impl := controller.NewImpl(rec, logger, queueName) + agentName := defaultControllerAgentName + + // Pass impl to the options. Save any optional results. + for _, fn := range optionsFns { + opts := fn(impl) + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + if opts.AgentName != "" { + agentName = opts.AgentName + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: kubeclient.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + versionedscheme.AddToScheme(scheme.Scheme) +} diff --git a/pkg/client/injection/reconciler/pipeline/v1alpha1/run/reconciler.go b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/reconciler.go new file mode 100644 index 00000000000..043e30d9acc --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/reconciler.go @@ -0,0 +1,352 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package run + +import ( + context "context" + json "encoding/json" + reflect "reflect" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + zap "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + cache "k8s.io/client-go/tools/cache" + record "k8s.io/client-go/tools/record" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1alpha1.Run. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1alpha1.Run. Any changes + // to the objects .Status or .Finalizers will be propagated to the stored + // object. It is recommended that implementors do not call any update calls + // for the Kind inside of ReconcileKind, it is the responsibility of the calling + // controller to propagate those properties. The resource passed to ReconcileKind + // will always have an empty deletion timestamp. + ReconcileKind(ctx context.Context, o *v1alpha1.Run) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1alpha1.Run. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1alpha1.Run. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1alpha1.Run) reconciler.Event +} + +// reconcilerImpl implements controller.Reconciler for v1alpha1.Run resources. +type reconcilerImpl struct { + // Client is used to write back status updates. + Client versioned.Interface + + // Listers index properties about resources + Lister pipelinev1alpha1.RunLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string +} + +// Check that our Reconciler implements controller.Reconciler +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister pipelinev1alpha1.RunLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // Check the options function input. It should be 0 or 1. + if len(options) > 1 { + logger.Fatalf("up to one options struct is supported, found %d", len(options)) + } + + rec := &reconcilerImpl{ + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // If configStore is set, attach the frozen configuration to the context. + if r.configStore != nil { + ctx = r.configStore.ToContext(ctx) + } + + // Add the recorder to context. + ctx = controller.WithEventRecorder(ctx, r.Recorder) + + // Convert the namespace/name string into a distinct namespace and name + + namespace, name, err := cache.SplitMetaNamespaceKey(key) + + if err != nil { + logger.Errorf("invalid resource key: %s", key) + return nil + } + + // Get the resource with this namespace/name. + + getter := r.Lister.Runs(namespace) + + original, err := getter.Get(name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing. + logger.Debugf("resource %q no longer exists", key) + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + if resource.GetDeletionTimestamp().IsZero() { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "ReconcileKind")) + + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + logger.Warnw("Failed to set finalizers", zap.Error(err)) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = r.reconciler.ReconcileKind(ctx, resource) + + } else if fin, ok := r.reconciler.(Finalizer); ok { + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", "FinalizeKind")) + + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = fin.FinalizeKind(ctx, resource) + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + logger.Warnw("Failed to clear finalizers", zap.Error(err)) + } + } + + // Synchronize the status. + if equality.Semantic.DeepEqual(original.Status, resource.Status) { + // If we didn't change anything then don't call updateStatus. + // This is important because the copy we loaded from the injectionInformer's + // cache may be stale and we don't want to overwrite a prior update + // to status with this stale state. + } else if err = r.updateStatus(original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed", + "Failed to update status for %q: %v", resource.Name, err) + return err + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + logger.Infow("Returned an event", zap.Any("event", reconcileEvent)) + r.Recorder.Eventf(resource, event.EventType, event.Reason, event.Format, event.Args...) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(existing *v1alpha1.Run, desired *v1alpha1.Run) error { + existing = existing.DeepCopy() + return reconciler.RetryUpdateConflicts(func(attempts int) (err error) { + // The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API. + if attempts > 0 { + + getter := r.Client.TektonV1alpha1().Runs(desired.Namespace) + + existing, err = getter.Get(desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if reflect.DeepEqual(existing.Status, desired.Status) { + return nil + } + + existing.Status = desired.Status + + updater := r.Client.TektonV1alpha1().Runs(existing.Namespace) + + _, err = updater.UpdateStatus(existing) + return err + }) +} + +// updateFinalizersFiltered will update the Finalizers of the resource. +// TODO: this method could be generic and sync all finalizers. For now it only +// updates defaultFinalizerName or its override. +func (r *reconcilerImpl) updateFinalizersFiltered(ctx context.Context, resource *v1alpha1.Run) (*v1alpha1.Run, error) { + + getter := r.Lister.Runs(resource.Namespace) + + actual, err := getter.Get(resource.Name) + if err != nil { + return resource, err + } + + // Don't modify the informers copy. + existing := actual.DeepCopy() + + var finalizers []string + + // If there's nothing to update, just return. + existingFinalizers := sets.NewString(existing.Finalizers...) + desiredFinalizers := sets.NewString(resource.Finalizers...) + + if desiredFinalizers.Has(r.finalizerName) { + if existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Add the finalizer. + finalizers = append(existing.Finalizers, r.finalizerName) + } else { + if !existingFinalizers.Has(r.finalizerName) { + // Nothing to do. + return resource, nil + } + // Remove the finalizer. + existingFinalizers.Delete(r.finalizerName) + finalizers = existingFinalizers.List() + } + + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "finalizers": finalizers, + "resourceVersion": existing.ResourceVersion, + }, + } + + patch, err := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.TektonV1alpha1().Runs(resource.Namespace) + + resource, err = patcher.Patch(resource.Name, types.MergePatchType, patch) + if err != nil { + r.Recorder.Eventf(resource, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resource.Name, err) + } else { + r.Recorder.Eventf(resource, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return resource, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1alpha1.Run) (*v1alpha1.Run, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + // If this resource is not being deleted, mark the finalizer. + if resource.GetDeletionTimestamp().IsZero() { + finalizers.Insert(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} + +func (r *reconcilerImpl) clearFinalizer(ctx context.Context, resource *v1alpha1.Run, reconcileEvent reconciler.Event) (*v1alpha1.Run, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + finalizers.Delete(r.finalizerName) + } + } + } else { + finalizers.Delete(r.finalizerName) + } + + resource.Finalizers = finalizers.List() + + // Synchronize the finalizers filtered by r.finalizerName. + return r.updateFinalizersFiltered(ctx, resource) +} diff --git a/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/controller.go b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/controller.go new file mode 100644 index 00000000000..144ec4f89d1 --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/controller.go @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package run + +import ( + context "context" + + run "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run" + v1alpha1run "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" + configmap "knative.dev/pkg/configmap" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// NewController creates a Reconciler for Run and returns the result of NewImpl. +func NewController( + ctx context.Context, + cmw configmap.Watcher, +) *controller.Impl { + logger := logging.FromContext(ctx) + + runInformer := run.Get(ctx) + + // TODO: setup additional informers here. + + r := &Reconciler{} + impl := v1alpha1run.NewImpl(ctx, r) + + logger.Info("Setting up event handlers.") + + runInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue)) + + // TODO: add additional informer event handlers here. + + return impl +} diff --git a/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/reconciler.go b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/reconciler.go new file mode 100644 index 00000000000..d7322fb125b --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1alpha1/run/stub/reconciler.go @@ -0,0 +1,66 @@ +/* +Copyright 2020 The Tekton 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 injection-gen. DO NOT EDIT. + +package run + +import ( + context "context" + + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + run "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" + v1 "k8s.io/api/core/v1" + reconciler "knative.dev/pkg/reconciler" +) + +// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT + +// newReconciledNormal makes a new reconciler event with event type Normal, and +// reason RunReconciled. +func newReconciledNormal(namespace, name string) reconciler.Event { + return reconciler.NewEvent(v1.EventTypeNormal, "RunReconciled", "Run reconciled: \"%s/%s\"", namespace, name) +} + +// Reconciler implements controller.Reconciler for Run resources. +type Reconciler struct { + // TODO: add additional requirements here. +} + +// Check that our Reconciler implements Interface +var _ run.Interface = (*Reconciler)(nil) + +// Optionally check that our Reconciler implements Finalizer +//var _ run.Finalizer = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, o *v1alpha1.Run) reconciler.Event { + // TODO: use this if the resource implements InitializeConditions. + // o.Status.InitializeConditions() + + // TODO: add custom reconciliation logic here. + + // TODO: use this if the object has .status.ObservedGeneration. + // o.Status.ObservedGeneration = o.Generation + return newReconciledNormal(o.Namespace, o.Name) +} + +// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called +// when the resource is deleted. +//func (r *Reconciler) FinalizeKind(ctx context.Context, o *v1alpha1.Run) reconciler.Event { +// // TODO: add custom finalization logic here. +// return nil +//} diff --git a/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go b/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go index 9e4b934db74..fc4bd080d6a 100644 --- a/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/pipeline/v1alpha1/expansion_generated.go @@ -46,6 +46,14 @@ type PipelineRunListerExpansion interface{} // PipelineRunNamespaceLister. type PipelineRunNamespaceListerExpansion interface{} +// RunListerExpansion allows custom methods to be added to +// RunLister. +type RunListerExpansion interface{} + +// RunNamespaceListerExpansion allows custom methods to be added to +// RunNamespaceLister. +type RunNamespaceListerExpansion interface{} + // TaskListerExpansion allows custom methods to be added to // TaskLister. type TaskListerExpansion interface{} diff --git a/pkg/client/listers/pipeline/v1alpha1/run.go b/pkg/client/listers/pipeline/v1alpha1/run.go new file mode 100644 index 00000000000..194d1a2bf5f --- /dev/null +++ b/pkg/client/listers/pipeline/v1alpha1/run.go @@ -0,0 +1,94 @@ +/* +Copyright 2020 The Tekton 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 v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// RunLister helps list Runs. +type RunLister interface { + // List lists all Runs in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Run, err error) + // Runs returns an object that can list and get Runs. + Runs(namespace string) RunNamespaceLister + RunListerExpansion +} + +// runLister implements the RunLister interface. +type runLister struct { + indexer cache.Indexer +} + +// NewRunLister returns a new RunLister. +func NewRunLister(indexer cache.Indexer) RunLister { + return &runLister{indexer: indexer} +} + +// List lists all Runs in the indexer. +func (s *runLister) List(selector labels.Selector) (ret []*v1alpha1.Run, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Run)) + }) + return ret, err +} + +// Runs returns an object that can list and get Runs. +func (s *runLister) Runs(namespace string) RunNamespaceLister { + return runNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// RunNamespaceLister helps list and get Runs. +type RunNamespaceLister interface { + // List lists all Runs in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.Run, err error) + // Get retrieves the Run from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.Run, error) + RunNamespaceListerExpansion +} + +// runNamespaceLister implements the RunNamespaceLister +// interface. +type runNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Runs in the indexer for a given namespace. +func (s runNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Run, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Run)) + }) + return ret, err +} + +// Get retrieves the Run from the indexer for a given namespace and name. +func (s runNamespaceLister) Get(name string) (*v1alpha1.Run, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("run"), name) + } + return obj.(*v1alpha1.Run), nil +}