diff --git a/pkg/apis/pipeline/v1/openapi_generated.go b/pkg/apis/pipeline/v1/openapi_generated.go index 903dbb2ae07..f0389784009 100644 --- a/pkg/apis/pipeline/v1/openapi_generated.go +++ b/pkg/apis/pipeline/v1/openapi_generated.go @@ -49,14 +49,25 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.ResolverRef": schema_pkg_apis_pipeline_v1_ResolverRef(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.ResultRef": schema_pkg_apis_pipeline_v1_ResultRef(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Sidecar": schema_pkg_apis_pipeline_v1_Sidecar(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.SidecarState": schema_pkg_apis_pipeline_v1_SidecarState(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Step": schema_pkg_apis_pipeline_v1_Step(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepOutputConfig": schema_pkg_apis_pipeline_v1_StepOutputConfig(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepState": schema_pkg_apis_pipeline_v1_StepState(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepTemplate": schema_pkg_apis_pipeline_v1_StepTemplate(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Task": schema_pkg_apis_pipeline_v1_Task(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskList": schema_pkg_apis_pipeline_v1_TaskList(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRef": schema_pkg_apis_pipeline_v1_TaskRef(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskResult": schema_pkg_apis_pipeline_v1_TaskResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRun": schema_pkg_apis_pipeline_v1_TaskRun(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunDebug": schema_pkg_apis_pipeline_v1_TaskRunDebug(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunInputs": schema_pkg_apis_pipeline_v1_TaskRunInputs(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunList": schema_pkg_apis_pipeline_v1_TaskRunList(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunResult": schema_pkg_apis_pipeline_v1_TaskRunResult(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSidecarOverride": schema_pkg_apis_pipeline_v1_TaskRunSidecarOverride(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSpec": schema_pkg_apis_pipeline_v1_TaskRunSpec(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus": schema_pkg_apis_pipeline_v1_TaskRunStatus(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatusFields": schema_pkg_apis_pipeline_v1_TaskRunStatusFields(ref), + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStepOverride": schema_pkg_apis_pipeline_v1_TaskRunStepOverride(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec": schema_pkg_apis_pipeline_v1_TaskSpec(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.WhenExpression": schema_pkg_apis_pipeline_v1_WhenExpression(ref), "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.WorkspaceBinding": schema_pkg_apis_pipeline_v1_WorkspaceBinding(ref), @@ -1569,6 +1580,57 @@ func schema_pkg_apis_pipeline_v1_Sidecar(ref common.ReferenceCallback) common.Op } } +func schema_pkg_apis_pipeline_v1_SidecarState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "SidecarState reports the results of running a sidecar in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + func schema_pkg_apis_pipeline_v1_Step(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1820,6 +1882,57 @@ func schema_pkg_apis_pipeline_v1_StepOutputConfig(ref common.ReferenceCallback) } } +func schema_pkg_apis_pipeline_v1_StepState(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "StepState reports the results of running a step in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "waiting": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a waiting container", + Ref: ref("k8s.io/api/core/v1.ContainerStateWaiting"), + }, + }, + "running": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a running container", + Ref: ref("k8s.io/api/core/v1.ContainerStateRunning"), + }, + }, + "terminated": { + SchemaProps: spec.SchemaProps{ + Description: "Details about a terminated container", + Ref: ref("k8s.io/api/core/v1.ContainerStateTerminated"), + }, + }, + "name": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "container": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "imageID": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ContainerStateRunning", "k8s.io/api/core/v1.ContainerStateTerminated", "k8s.io/api/core/v1.ContainerStateWaiting"}, + } +} + func schema_pkg_apis_pipeline_v1_StepTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2169,6 +2282,167 @@ func schema_pkg_apis_pipeline_v1_TaskResult(ref common.ReferenceCallback) common } } +func schema_pkg_apis_pipeline_v1_TaskRun(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRun represents a single execution of a Task. TaskRuns are how the steps specified in a Task are executed; they specify the parameters and resources used to run the steps in a Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunDebug(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunDebug defines the breakpoint config for a particular TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "breakpoint": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunInputs(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunInputs holds the input values that this task was invoked with.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "params": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Param"), + }, + }, + }, + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Param"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunList contains a list of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "metadata": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRun"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRun", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, + } +} + func schema_pkg_apis_pipeline_v1_TaskRunResult(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2207,6 +2481,492 @@ func schema_pkg_apis_pipeline_v1_TaskRunResult(ref common.ReferenceCallback) com } } +func schema_pkg_apis_pipeline_v1_TaskRunSidecarOverride(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the Sidecar to override.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "The resource requirements to apply to the Sidecar.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + }, + Required: []string{"name", "resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunSpec defines the desired state of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "debug": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunDebug"), + }, + }, + "params": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Param"), + }, + }, + }, + }, + }, + "serviceAccountName": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "taskRef": { + SchemaProps: spec.SchemaProps{ + Description: "no more than one of the TaskRef and TaskSpec may be specified.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRef"), + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec"), + }, + }, + "status": { + SchemaProps: spec.SchemaProps{ + Description: "Used for cancelling a taskrun (and maybe more later on)", + Type: []string{"string"}, + Format: "", + }, + }, + "statusMessage": { + SchemaProps: spec.SchemaProps{ + Description: "Status message for cancellation.", + Type: []string{"string"}, + Format: "", + }, + }, + "timeout": { + SchemaProps: spec.SchemaProps{ + Description: "Time after which the build times out. Defaults to 1 hour. Specified build timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"), + }, + }, + "podTemplate": { + SchemaProps: spec.SchemaProps{ + Description: "PodTemplate holds pod specific configuration", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template"), + }, + }, + "workspaces": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Workspaces is a list of WorkspaceBindings from volumes to workspaces.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.WorkspaceBinding"), + }, + }, + }, + }, + }, + "stepOverrides": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Overrides to apply to Steps in this TaskRun. If a field is specified in both a Step and a StepOverride, the value from the StepOverride will be used. This field is only supported when the alpha feature gate is enabled.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStepOverride"), + }, + }, + }, + }, + }, + "sidecarOverrides": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Overrides to apply to Sidecars in this TaskRun. If a field is specified in both a Sidecar and a SidecarOverride, the value from the SidecarOverride will be used. This field is only supported when the alpha feature gate is enabled.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSidecarOverride"), + }, + }, + }, + }, + }, + "computeResources": { + SchemaProps: spec.SchemaProps{ + Description: "Compute resources to use for this TaskRun", + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod.Template", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.Param", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRef", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunDebug", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunSidecarOverride", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStepOverride", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.WorkspaceBinding", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunStatus(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStatus defines the observed state of TaskRun", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "observedGeneration": { + SchemaProps: spec.SchemaProps{ + Description: "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + Type: []string{"integer"}, + Format: "int64", + }, + }, + "conditions": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Conditions the latest available observations of a resource's current state.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("knative.dev/pkg/apis.Condition"), + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the build is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the build completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "steps": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Steps describes the state of each build step container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepState"), + }, + }, + }, + }, + }, + "retriesStatus": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus"), + }, + }, + }, + }, + }, + "results": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Results are the list of results written out by the task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunResult"), + }, + }, + }, + }, + }, + "sidecars": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.SidecarState"), + }, + }, + }, + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.SidecarState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.Time", "knative.dev/pkg/apis.Condition"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunStatusFields(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStatusFields holds the fields of TaskRun's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "podName": { + SchemaProps: spec.SchemaProps{ + Description: "PodName is the name of the pod responsible for executing this task's steps.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "startTime": { + SchemaProps: spec.SchemaProps{ + Description: "StartTime is the time the build is actually started.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "completionTime": { + SchemaProps: spec.SchemaProps{ + Description: "CompletionTime is the time the build completed.", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), + }, + }, + "steps": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Steps describes the state of each build step container.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepState"), + }, + }, + }, + }, + }, + "retriesStatus": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus"), + }, + }, + }, + }, + }, + "results": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "Results are the list of results written out by the task's containers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunResult"), + }, + }, + }, + }, + }, + "sidecars": { + VendorExtensible: spec.VendorExtensible{ + Extensions: spec.Extensions{ + "x-kubernetes-list-type": "atomic", + }, + }, + SchemaProps: spec.SchemaProps{ + Description: "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.SidecarState"), + }, + }, + }, + }, + }, + "taskSpec": { + SchemaProps: spec.SchemaProps{ + Description: "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec"), + }, + }, + }, + Required: []string{"podName"}, + }, + }, + Dependencies: []string{ + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.SidecarState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.StepState", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunResult", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskRunStatus", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1.TaskSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + } +} + +func schema_pkg_apis_pipeline_v1_TaskRunStepOverride(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TaskRunStepOverride is used to override the values of a Step in the corresponding Task.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Description: "The name of the Step to override.", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "resources": { + SchemaProps: spec.SchemaProps{ + Description: "The resource requirements to apply to the Step.", + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + }, + Required: []string{"name", "resources"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.ResourceRequirements"}, + } +} + func schema_pkg_apis_pipeline_v1_TaskSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/pipeline/v1/swagger.json b/pkg/apis/pipeline/v1/swagger.json index 91e2684773b..e09dc8eb7b2 100644 --- a/pkg/apis/pipeline/v1/swagger.json +++ b/pkg/apis/pipeline/v1/swagger.json @@ -813,6 +813,33 @@ } } }, + "v1.SidecarState": { + "description": "SidecarState reports the results of running a sidecar in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, "v1.Step": { "description": "Step runs a subcomponent of a Task", "type": "object", @@ -947,6 +974,33 @@ } } }, + "v1.StepState": { + "description": "StepState reports the results of running a step in a Task.", + "type": "object", + "properties": { + "container": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "running": { + "description": "Details about a running container", + "$ref": "#/definitions/v1.ContainerStateRunning" + }, + "terminated": { + "description": "Details about a terminated container", + "$ref": "#/definitions/v1.ContainerStateTerminated" + }, + "waiting": { + "description": "Details about a waiting container", + "$ref": "#/definitions/v1.ContainerStateWaiting" + } + } + }, "v1.StepTemplate": { "description": "StepTemplate is a template for a Step", "type": "object", @@ -1133,6 +1187,88 @@ } } }, + "v1.TaskRun": { + "description": "TaskRun represents a single execution of a Task. TaskRuns are how the steps specified in a Task are executed; they specify the parameters and resources used to run the steps in a Task.", + "type": "object", + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ObjectMeta" + }, + "spec": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunSpec" + }, + "status": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunStatus" + } + } + }, + "v1.TaskRunDebug": { + "description": "TaskRunDebug defines the breakpoint config for a particular TaskRun", + "type": "object", + "properties": { + "breakpoint": { + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "v1.TaskRunInputs": { + "description": "TaskRunInputs holds the input values that this task was invoked with.", + "type": "object", + "properties": { + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Param" + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "v1.TaskRunList": { + "description": "TaskRunList contains a list of TaskRun", + "type": "object", + "required": [ + "items" + ], + "properties": { + "apiVersion": { + "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRun" + } + }, + "kind": { + "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string" + }, + "metadata": { + "default": {}, + "$ref": "#/definitions/v1.ListMeta" + } + } + }, "v1.TaskRunResult": { "description": "TaskRunResult used to describe the results of a task", "type": "object", @@ -1157,6 +1293,268 @@ } } }, + "v1.TaskRunSidecarOverride": { + "description": "TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task.", + "type": "object", + "required": [ + "name", + "resources" + ], + "properties": { + "name": { + "description": "The name of the Sidecar to override.", + "type": "string", + "default": "" + }, + "resources": { + "description": "The resource requirements to apply to the Sidecar.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, + "v1.TaskRunSpec": { + "description": "TaskRunSpec defines the desired state of TaskRun", + "type": "object", + "properties": { + "computeResources": { + "description": "Compute resources to use for this TaskRun", + "$ref": "#/definitions/v1.ResourceRequirements" + }, + "debug": { + "$ref": "#/definitions/v1.TaskRunDebug" + }, + "params": { + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.Param" + }, + "x-kubernetes-list-type": "atomic" + }, + "podTemplate": { + "description": "PodTemplate holds pod specific configuration", + "$ref": "#/definitions/pod.Template" + }, + "serviceAccountName": { + "type": "string", + "default": "" + }, + "sidecarOverrides": { + "description": "Overrides to apply to Sidecars in this TaskRun. If a field is specified in both a Sidecar and a SidecarOverride, the value from the SidecarOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunSidecarOverride" + }, + "x-kubernetes-list-type": "atomic" + }, + "status": { + "description": "Used for cancelling a taskrun (and maybe more later on)", + "type": "string" + }, + "statusMessage": { + "description": "Status message for cancellation.", + "type": "string" + }, + "stepOverrides": { + "description": "Overrides to apply to Steps in this TaskRun. If a field is specified in both a Step and a StepOverride, the value from the StepOverride will be used. This field is only supported when the alpha feature gate is enabled.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunStepOverride" + }, + "x-kubernetes-list-type": "atomic" + }, + "taskRef": { + "description": "no more than one of the TaskRef and TaskSpec may be specified.", + "$ref": "#/definitions/v1.TaskRef" + }, + "taskSpec": { + "$ref": "#/definitions/v1.TaskSpec" + }, + "timeout": { + "description": "Time after which the build times out. Defaults to 1 hour. Specified build timeout should be less than 24h. Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration", + "$ref": "#/definitions/v1.Duration" + }, + "workspaces": { + "description": "Workspaces is a list of WorkspaceBindings from volumes to workspaces.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.WorkspaceBinding" + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "v1.TaskRunStatus": { + "description": "TaskRunStatus defines the observed state of TaskRun", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "annotations": { + "description": "Annotations is additional Status fields for the Resource to save some additional State as well as convey more information to the user. This is roughly akin to Annotations on any k8s resource, just the reconciler conveying richer information outwards.", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "conditions": { + "description": "Conditions the latest available observations of a resource's current state.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/knative.Condition" + }, + "x-kubernetes-patch-merge-key": "type", + "x-kubernetes-patch-strategy": "merge" + }, + "observedGeneration": { + "description": "ObservedGeneration is the 'Generation' of the Service that was last processed by the controller.", + "type": "integer", + "format": "int64" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "results": { + "description": "Results are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunResult" + }, + "x-kubernetes-list-type": "atomic" + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunStatus" + }, + "x-kubernetes-list-type": "atomic" + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.SidecarState" + }, + "x-kubernetes-list-type": "atomic" + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.StepState" + }, + "x-kubernetes-list-type": "atomic" + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1.TaskSpec" + } + } + }, + "v1.TaskRunStatusFields": { + "description": "TaskRunStatusFields holds the fields of TaskRun's status. This is defined separately and inlined so that other types can readily consume these fields via duck typing.", + "type": "object", + "required": [ + "podName" + ], + "properties": { + "completionTime": { + "description": "CompletionTime is the time the build completed.", + "$ref": "#/definitions/v1.Time" + }, + "podName": { + "description": "PodName is the name of the pod responsible for executing this task's steps.", + "type": "string", + "default": "" + }, + "results": { + "description": "Results are the list of results written out by the task's containers", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunResult" + }, + "x-kubernetes-list-type": "atomic" + }, + "retriesStatus": { + "description": "RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.TaskRunStatus" + }, + "x-kubernetes-list-type": "atomic" + }, + "sidecars": { + "description": "The list has one entry per sidecar in the manifest. Each entry is represents the imageid of the corresponding sidecar.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.SidecarState" + }, + "x-kubernetes-list-type": "atomic" + }, + "startTime": { + "description": "StartTime is the time the build is actually started.", + "$ref": "#/definitions/v1.Time" + }, + "steps": { + "description": "Steps describes the state of each build step container.", + "type": "array", + "items": { + "default": {}, + "$ref": "#/definitions/v1.StepState" + }, + "x-kubernetes-list-type": "atomic" + }, + "taskSpec": { + "description": "TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun.", + "$ref": "#/definitions/v1.TaskSpec" + } + } + }, + "v1.TaskRunStepOverride": { + "description": "TaskRunStepOverride is used to override the values of a Step in the corresponding Task.", + "type": "object", + "required": [ + "name", + "resources" + ], + "properties": { + "name": { + "description": "The name of the Step to override.", + "type": "string", + "default": "" + }, + "resources": { + "description": "The resource requirements to apply to the Step.", + "default": {}, + "$ref": "#/definitions/v1.ResourceRequirements" + } + } + }, "v1.TaskSpec": { "description": "TaskSpec defines the desired state of Task.", "type": "object", diff --git a/pkg/apis/pipeline/v1/taskrun_defaults.go b/pkg/apis/pipeline/v1/taskrun_defaults.go new file mode 100644 index 00000000000..61932f46688 --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_defaults.go @@ -0,0 +1,73 @@ +/* +Copyright 2022 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 v1 + +import ( + "context" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*TaskRun)(nil) + +// ManagedByLabelKey is the label key used to mark what is managing this resource +const ManagedByLabelKey = "app.kubernetes.io/managed-by" + +// SetDefaults implements apis.Defaultable +func (tr *TaskRun) SetDefaults(ctx context.Context) { + ctx = apis.WithinParent(ctx, tr.ObjectMeta) + tr.Spec.SetDefaults(ctx) + + // If the TaskRun doesn't have a managed-by label, apply the default + // specified in the config. + cfg := config.FromContextOrDefaults(ctx) + if tr.ObjectMeta.Labels == nil { + tr.ObjectMeta.Labels = map[string]string{} + } + if _, found := tr.ObjectMeta.Labels[ManagedByLabelKey]; !found { + tr.ObjectMeta.Labels[ManagedByLabelKey] = cfg.Defaults.DefaultManagedByLabelValue + } +} + +// SetDefaults implements apis.Defaultable +func (trs *TaskRunSpec) SetDefaults(ctx context.Context) { + cfg := config.FromContextOrDefaults(ctx) + if trs.TaskRef != nil && trs.TaskRef.Kind == "" { + trs.TaskRef.Kind = NamespacedTaskKind + } + + if trs.Timeout == nil { + trs.Timeout = &metav1.Duration{Duration: time.Duration(cfg.Defaults.DefaultTimeoutMinutes) * time.Minute} + } + + defaultSA := cfg.Defaults.DefaultServiceAccount + if trs.ServiceAccountName == "" && defaultSA != "" { + trs.ServiceAccountName = defaultSA + } + + defaultPodTemplate := cfg.Defaults.DefaultPodTemplate + trs.PodTemplate = pod.MergePodTemplateWithDefault(trs.PodTemplate, defaultPodTemplate) + + // If this taskrun has an embedded task, apply the usual task defaults + if trs.TaskSpec != nil { + trs.TaskSpec.SetDefaults(ctx) + } +} diff --git a/pkg/apis/pipeline/v1/taskrun_defaults_test.go b/pkg/apis/pipeline/v1/taskrun_defaults_test.go new file mode 100644 index 00000000000..14f1c5b6278 --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_defaults_test.go @@ -0,0 +1,416 @@ +/* +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 v1_test + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "github.com/tektoncd/pipeline/test/diff" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + logtesting "knative.dev/pkg/logging/testing" +) + +var ( + ignoreUnexportedResources = cmpopts.IgnoreUnexported() + ttrue = true +) + +func TestTaskRunSpec_SetDefaults(t *testing.T) { + cases := []struct { + desc string + trs *v1.TaskRunSpec + want *v1.TaskRunSpec + }{{ + desc: "taskref is nil", + trs: &v1.TaskRunSpec{ + TaskRef: nil, + Timeout: &metav1.Duration{Duration: 500 * time.Millisecond}, + }, + want: &v1.TaskRunSpec{ + TaskRef: nil, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: 500 * time.Millisecond}, + }, + }, { + desc: "taskref kind is empty", + trs: &v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{}, + Timeout: &metav1.Duration{Duration: 500 * time.Millisecond}, + }, + want: &v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Kind: v1.NamespacedTaskKind}, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: 500 * time.Millisecond}, + }, + }, { + desc: "pod template is nil", + trs: &v1.TaskRunSpec{}, + want: &v1.TaskRunSpec{ + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, + }, + }, { + desc: "pod template is not nil", + trs: &v1.TaskRunSpec{ + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label": "value", + }, + }, + }, + want: &v1.TaskRunSpec{ + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label": "value", + }, + }, + }, + }, { + desc: "embedded taskSpec", + trs: &v1.TaskRunSpec{ + TaskSpec: &v1.TaskSpec{ + Params: []v1.ParamSpec{{ + Name: "param-name", + }}, + }, + }, + want: &v1.TaskRunSpec{ + TaskSpec: &v1.TaskSpec{ + Params: []v1.ParamSpec{{ + Name: "param-name", + Type: v1.ParamTypeString, + }}, + }, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, + }, + }} + for _, tc := range cases { + t.Run(tc.desc, func(t *testing.T) { + ctx := context.Background() + tc.trs.SetDefaults(ctx) + + if d := cmp.Diff(tc.want, tc.trs); d != "" { + t.Errorf("Mismatch of TaskRunSpec: %s", diff.PrintWantGot(d)) + } + }) + } +} + +func TestTaskRunDefaulting(t *testing.T) { + tests := []struct { + name string + in *v1.TaskRun + want *v1.TaskRun + wc func(context.Context) context.Context + }{{ + name: "empty no context", + in: &v1.TaskRun{}, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, + }, + }, + }, { + name: "TaskRef default to namespace kind", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}, + }, + }, + }, { + name: "TaskRef default config context", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + ServiceAccountName: config.DefaultServiceAccountValue, + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRef default config context with SA", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + ServiceAccountName: "tekton", + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-service-account": "tekton", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRun managed-by set in config", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "something-else"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-managed-by-label-value": "something-else", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRun managed-by set in request and config (request wins)", + in: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "user-specified"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "user-specified"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + ServiceAccountName: config.DefaultServiceAccountValue, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-managed-by-label-value": "something-else", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRef pod template is coming from default config pod template", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + ServiceAccountName: "tekton", + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label": "value", + }, + }, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-service-account": "tekton", + "default-pod-template": "nodeSelector: { 'label': 'value' }", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRef pod template NodeSelector takes precedence over default config pod template NodeSelector", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label2": "value2", + }, + }, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + ServiceAccountName: "tekton", + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label2": "value2", + }, + }, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-service-account": "tekton", + "default-pod-template": "nodeSelector: { 'label': 'value' }", + }, + }) + return s.ToContext(ctx) + }, + }, { + name: "TaskRef pod template merges non competing fields with default config pod template", + in: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo"}, + PodTemplate: &pod.Template{ + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: &ttrue, + }, + }, + }, + }, + want: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app.kubernetes.io/managed-by": "tekton-pipelines"}, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{Name: "foo", Kind: v1.NamespacedTaskKind}, + Timeout: &metav1.Duration{Duration: 5 * time.Minute}, + ServiceAccountName: "tekton", + PodTemplate: &pod.Template{ + NodeSelector: map[string]string{ + "label": "value", + }, + SecurityContext: &corev1.PodSecurityContext{ + RunAsNonRoot: &ttrue, + }, + }, + }, + }, + wc: func(ctx context.Context) context.Context { + s := config.NewStore(logtesting.TestLogger(t)) + s.OnConfigChanged(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: config.GetDefaultsConfigName(), + }, + Data: map[string]string{ + "default-timeout-minutes": "5", + "default-service-account": "tekton", + "default-pod-template": "nodeSelector: { 'label': 'value' }", + }, + }) + return s.ToContext(ctx) + }, + }} + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := tc.in + ctx := context.Background() + if tc.wc != nil { + ctx = tc.wc(ctx) + } + got.SetDefaults(ctx) + if !cmp.Equal(got, tc.want, ignoreUnexportedResources) { + d := cmp.Diff(got, tc.want, ignoreUnexportedResources) + t.Errorf("SetDefaults %s", diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/apis/pipeline/v1/taskrun_types.go b/pkg/apis/pipeline/v1/taskrun_types.go new file mode 100644 index 00000000000..d0076f7936a --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_types.go @@ -0,0 +1,433 @@ +/* +Copyright 2022 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 v1 + +import ( + "context" + "fmt" + "time" + + "github.com/tektoncd/pipeline/pkg/apis/config" + apisconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +// TaskRunSpec defines the desired state of TaskRun +type TaskRunSpec struct { + // +optional + Debug *TaskRunDebug `json:"debug,omitempty"` + // +optional + // +listType=atomic + Params []Param `json:"params,omitempty"` + // +optional + ServiceAccountName string `json:"serviceAccountName"` + // no more than one of the TaskRef and TaskSpec may be specified. + // +optional + TaskRef *TaskRef `json:"taskRef,omitempty"` + // +optional + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` + // Used for cancelling a taskrun (and maybe more later on) + // +optional + Status TaskRunSpecStatus `json:"status,omitempty"` + // Status message for cancellation. + // +optional + StatusMessage TaskRunSpecStatusMessage `json:"statusMessage,omitempty"` + // Time after which the build times out. Defaults to 1 hour. + // Specified build timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + // PodTemplate holds pod specific configuration + PodTemplate *pod.PodTemplate `json:"podTemplate,omitempty"` + // Workspaces is a list of WorkspaceBindings from volumes to workspaces. + // +optional + // +listType=atomic + Workspaces []WorkspaceBinding `json:"workspaces,omitempty"` + // Overrides to apply to Steps in this TaskRun. + // If a field is specified in both a Step and a StepOverride, + // the value from the StepOverride will be used. + // This field is only supported when the alpha feature gate is enabled. + // +optional + // +listType=atomic + StepOverrides []TaskRunStepOverride `json:"stepOverrides,omitempty"` + // Overrides to apply to Sidecars in this TaskRun. + // If a field is specified in both a Sidecar and a SidecarOverride, + // the value from the SidecarOverride will be used. + // This field is only supported when the alpha feature gate is enabled. + // +optional + // +listType=atomic + SidecarOverrides []TaskRunSidecarOverride `json:"sidecarOverrides,omitempty"` + // Compute resources to use for this TaskRun + ComputeResources *corev1.ResourceRequirements `json:"computeResources,omitempty"` +} + +// TaskRunSpecStatus defines the taskrun spec status the user can provide +type TaskRunSpecStatus string + +const ( + // TaskRunSpecStatusCancelled indicates that the user wants to cancel the task, + // if not already cancelled or terminated + TaskRunSpecStatusCancelled = "TaskRunCancelled" +) + +// TaskRunSpecStatusMessage defines human readable status messages for the TaskRun. +type TaskRunSpecStatusMessage string + +const ( + // TaskRunCancelledByPipelineMsg indicates that the PipelineRun of which this + // TaskRun was a part of has been cancelled. + TaskRunCancelledByPipelineMsg TaskRunSpecStatusMessage = "TaskRun cancelled as the PipelineRun it belongs to has been cancelled." +) + +// TaskRunDebug defines the breakpoint config for a particular TaskRun +type TaskRunDebug struct { + // +optional + // +listType=atomic + Breakpoint []string `json:"breakpoint,omitempty"` +} + +// TaskRunInputs holds the input values that this task was invoked with. +type TaskRunInputs struct { + // +optional + // +listType=atomic + Params []Param `json:"params,omitempty"` +} + +var taskRunCondSet = apis.NewBatchConditionSet() + +// TaskRunStatus defines the observed state of TaskRun +type TaskRunStatus struct { + duckv1.Status `json:",inline"` + + // TaskRunStatusFields inlines the status fields. + TaskRunStatusFields `json:",inline"` +} + +// TaskRunReason is an enum used to store all TaskRun reason for +// the Succeeded condition that are controlled by the TaskRun itself. Failure +// reasons that emerge from underlying resources are not included here +type TaskRunReason string + +const ( + // TaskRunReasonStarted is the reason set when the TaskRun has just started + TaskRunReasonStarted TaskRunReason = "Started" + // TaskRunReasonRunning is the reason set when the TaskRun is running + TaskRunReasonRunning TaskRunReason = "Running" + // TaskRunReasonSuccessful is the reason set when the TaskRun completed successfully + TaskRunReasonSuccessful TaskRunReason = "Succeeded" + // TaskRunReasonFailed is the reason set when the TaskRun completed with a failure + TaskRunReasonFailed TaskRunReason = "Failed" + // TaskRunReasonCancelled is the reason set when the Taskrun is cancelled by the user + TaskRunReasonCancelled TaskRunReason = "TaskRunCancelled" + // TaskRunReasonTimedOut is the reason set when the Taskrun has timed out + TaskRunReasonTimedOut TaskRunReason = "TaskRunTimeout" + // TaskRunReasonResolvingTaskRef indicates that the TaskRun is waiting for + // its taskRef to be asynchronously resolved. + TaskRunReasonResolvingTaskRef = "ResolvingTaskRef" + // TaskRunReasonImagePullFailed is the reason set when the step of a task fails due to image not being pulled + TaskRunReasonImagePullFailed TaskRunReason = "TaskRunImagePullFailed" +) + +func (t TaskRunReason) String() string { + return string(t) +} + +// GetStartedReason returns the reason set to the "Succeeded" condition when +// InitializeConditions is invoked +func (trs *TaskRunStatus) GetStartedReason() string { + return TaskRunReasonStarted.String() +} + +// GetRunningReason returns the reason set to the "Succeeded" condition when +// the TaskRun starts running. This is used indicate that the resource +// could be validated is starting to perform its job. +func (trs *TaskRunStatus) GetRunningReason() string { + return TaskRunReasonRunning.String() +} + +// MarkResourceOngoing sets the ConditionSucceeded condition to ConditionUnknown +// with the reason and message. +func (trs *TaskRunStatus) MarkResourceOngoing(reason TaskRunReason, message string) { + taskRunCondSet.Manage(trs).SetCondition(apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionUnknown, + Reason: reason.String(), + Message: message, + }) +} + +// MarkResourceFailed sets the ConditionSucceeded condition to ConditionFalse +// based on an error that occurred and a reason +func (trs *TaskRunStatus) MarkResourceFailed(reason TaskRunReason, err error) { + taskRunCondSet.Manage(trs).SetCondition(apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Reason: reason.String(), + Message: err.Error(), + }) + succeeded := trs.GetCondition(apis.ConditionSucceeded) + trs.CompletionTime = &succeeded.LastTransitionTime.Inner +} + +// TaskRunStatusFields holds the fields of TaskRun's status. This is defined +// separately and inlined so that other types can readily consume these fields +// via duck typing. +type TaskRunStatusFields struct { + // PodName is the name of the pod responsible for executing this task's steps. + PodName string `json:"podName"` + + // 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"` + + // Steps describes the state of each build step container. + // +optional + // +listType=atomic + Steps []StepState `json:"steps,omitempty"` + + // RetriesStatus contains the history of TaskRunStatus in case of a retry in order to keep record of failures. + // All TaskRunStatus stored in RetriesStatus will have no date within the RetriesStatus as is redundant. + // +optional + // +listType=atomic + RetriesStatus []TaskRunStatus `json:"retriesStatus,omitempty"` + + // Results are the list of results written out by the task's containers + // +optional + // +listType=atomic + Results []TaskRunResult `json:"results,omitempty"` + + // The list has one entry per sidecar in the manifest. Each entry is + // represents the imageid of the corresponding sidecar. + // +listType=atomic + Sidecars []SidecarState `json:"sidecars,omitempty"` + + // TaskSpec contains the Spec from the dereferenced Task definition used to instantiate this TaskRun. + TaskSpec *TaskSpec `json:"taskSpec,omitempty"` +} + +// TaskRunStepOverride is used to override the values of a Step in the corresponding Task. +type TaskRunStepOverride struct { + // The name of the Step to override. + Name string `json:"name"` + // The resource requirements to apply to the Step. + Resources corev1.ResourceRequirements `json:"resources"` +} + +// TaskRunSidecarOverride is used to override the values of a Sidecar in the corresponding Task. +type TaskRunSidecarOverride struct { + // The name of the Sidecar to override. + Name string `json:"name"` + // The resource requirements to apply to the Sidecar. + Resources corev1.ResourceRequirements `json:"resources"` +} + +// GetGroupVersionKind implements kmeta.OwnerRefable. +func (*TaskRun) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind(pipeline.TaskRunControllerName) +} + +// GetStatusCondition returns the task run status as a ConditionAccessor +func (tr *TaskRun) GetStatusCondition() apis.ConditionAccessor { + return &tr.Status +} + +// GetCondition returns the Condition matching the given type. +func (trs *TaskRunStatus) GetCondition(t apis.ConditionType) *apis.Condition { + return taskRunCondSet.Manage(trs).GetCondition(t) +} + +// InitializeConditions will set all conditions in taskRunCondSet to unknown for the TaskRun +// and set the started time to the current time +func (trs *TaskRunStatus) InitializeConditions() { + started := false + if trs.StartTime.IsZero() { + trs.StartTime = &metav1.Time{Time: time.Now()} + started = true + } + conditionManager := taskRunCondSet.Manage(trs) + conditionManager.InitializeConditions() + // Ensure the started reason is set for the "Succeeded" condition + if started { + initialCondition := conditionManager.GetCondition(apis.ConditionSucceeded) + initialCondition.Reason = TaskRunReasonStarted.String() + conditionManager.SetCondition(*initialCondition) + } +} + +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. +func (trs *TaskRunStatus) SetCondition(newCond *apis.Condition) { + if newCond != nil { + taskRunCondSet.Manage(trs).SetCondition(*newCond) + } +} + +// StepState reports the results of running a step in a Task. +type StepState struct { + corev1.ContainerState `json:",inline"` + Name string `json:"name,omitempty"` + Container string `json:"container,omitempty"` + ImageID string `json:"imageID,omitempty"` +} + +// SidecarState reports the results of running a sidecar in a Task. +type SidecarState struct { + corev1.ContainerState `json:",inline"` + Name string `json:"name,omitempty"` + Container string `json:"container,omitempty"` + ImageID string `json:"imageID,omitempty"` +} + +// +genclient +// +genreconciler:krshapedlogic=false +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRun represents a single execution of a Task. TaskRuns are how the steps +// specified in a Task are executed; they specify the parameters and resources +// used to run the steps in a Task. +// +// +k8s:openapi-gen=true +type TaskRun struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +optional + Spec TaskRunSpec `json:"spec,omitempty"` + // +optional + Status TaskRunStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// TaskRunList contains a list of TaskRun +type TaskRunList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []TaskRun `json:"items"` +} + +// GetPipelineRunPVCName for taskrun gets pipelinerun +func (tr *TaskRun) GetPipelineRunPVCName() string { + if tr == nil { + return "" + } + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return fmt.Sprintf("%s-pvc", ref.Name) + } + } + return "" +} + +// HasPipelineRunOwnerReference returns true of TaskRun has +// owner reference of type PipelineRun +func (tr *TaskRun) HasPipelineRunOwnerReference() bool { + for _, ref := range tr.GetOwnerReferences() { + if ref.Kind == pipeline.PipelineRunControllerName { + return true + } + } + return false +} + +// IsDone returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsDone() bool { + return !tr.Status.GetCondition(apis.ConditionSucceeded).IsUnknown() +} + +// HasStarted function check whether taskrun has valid start time set in its status +func (tr *TaskRun) HasStarted() bool { + return tr.Status.StartTime != nil && !tr.Status.StartTime.IsZero() +} + +// IsSuccessful returns true if the TaskRun's status indicates that it is done. +func (tr *TaskRun) IsSuccessful() bool { + return tr != nil && tr.Status.GetCondition(apis.ConditionSucceeded).IsTrue() +} + +// IsCancelled returns true if the TaskRun's spec status is set to Cancelled state +func (tr *TaskRun) IsCancelled() bool { + return tr.Spec.Status == TaskRunSpecStatusCancelled +} + +// HasTimedOut returns true if the TaskRun runtime is beyond the allowed timeout +func (tr *TaskRun) HasTimedOut(ctx context.Context, c clock.PassiveClock) bool { + if tr.Status.StartTime.IsZero() { + return false + } + timeout := tr.GetTimeout(ctx) + // If timeout is set to 0 or defaulted to 0, there is no timeout. + if timeout == apisconfig.NoTimeoutDuration { + return false + } + runtime := c.Since(tr.Status.StartTime.Time) + return runtime > timeout +} + +// GetTimeout returns the timeout for the TaskRun, or the default if not specified +func (tr *TaskRun) GetTimeout(ctx context.Context) time.Duration { + // Use the platform default is no timeout is set + if tr.Spec.Timeout == nil { + defaultTimeout := time.Duration(config.FromContextOrDefaults(ctx).Defaults.DefaultTimeoutMinutes) + return defaultTimeout * time.Minute + } + return tr.Spec.Timeout.Duration +} + +// GetNamespacedName returns a k8s namespaced name that identifies this TaskRun +func (tr *TaskRun) GetNamespacedName() types.NamespacedName { + return types.NamespacedName{Namespace: tr.Namespace, Name: tr.Name} +} + +// IsPartOfPipeline return true if TaskRun is a part of a Pipeline. +// It also return the name of Pipeline and PipelineRun +func (tr *TaskRun) IsPartOfPipeline() (bool, string, string) { + if tr == nil || len(tr.Labels) == 0 { + return false, "", "" + } + + if pl, ok := tr.Labels[pipeline.PipelineLabelKey]; ok { + return true, pl, tr.Labels[pipeline.PipelineRunLabelKey] + } + + return false, "", "" +} + +// HasVolumeClaimTemplate returns true if TaskRun contains volumeClaimTemplates that is +// used for creating PersistentVolumeClaims with an OwnerReference for each run +func (tr *TaskRun) HasVolumeClaimTemplate() bool { + for _, ws := range tr.Spec.Workspaces { + if ws.VolumeClaimTemplate != nil { + return true + } + } + return false +} diff --git a/pkg/apis/pipeline/v1/taskrun_types_test.go b/pkg/apis/pipeline/v1/taskrun_types_test.go new file mode 100644 index 00000000000..d37a76d3c3d --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_types_test.go @@ -0,0 +1,381 @@ +/* +Copyright 2022 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 v1_test + +import ( + "context" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/pipeline" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "github.com/tektoncd/pipeline/test/diff" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/clock" + "knative.dev/pkg/apis" + duckv1 "knative.dev/pkg/apis/duck/v1" +) + +var now = time.Date(2022, time.January, 1, 0, 0, 0, 0, time.UTC) +var testClock = clock.NewFakePassiveClock(now) + +func TestTaskRun_GetPipelineRunPVCName(t *testing.T) { + tests := []struct { + name string + tr *v1.TaskRun + expectedPVCName string + }{{ + name: "invalid owner reference", + tr: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{{ + Kind: "SomeOtherOwner", + Name: "testpr", + }}, + }, + }, + expectedPVCName: "", + }, { + name: "valid pipelinerun owner", + tr: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{{ + Kind: "PipelineRun", + Name: "testpr", + }}, + }, + }, + expectedPVCName: "testpr-pvc", + }, { + name: "nil taskrun", + expectedPVCName: "", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.tr.GetPipelineRunPVCName() != tt.expectedPVCName { + t.Fatalf("taskrun pipeline run pvc name mismatch: got %s ; expected %s", tt.tr.GetPipelineRunPVCName(), tt.expectedPVCName) + } + }) + } +} + +func TestTaskRun_HasPipelineRun(t *testing.T) { + tests := []struct { + name string + tr *v1.TaskRun + want bool + }{{ + name: "invalid owner reference", + tr: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{{ + Kind: "SomeOtherOwner", + Name: "testpr", + }}, + }, + }, + want: false, + }, { + name: "valid pipelinerun owner", + tr: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + OwnerReferences: []metav1.OwnerReference{{ + Kind: "PipelineRun", + Name: "testpr", + }}, + }, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.tr.HasPipelineRunOwnerReference() != tt.want { + t.Fatalf("taskrun pipeline run pvc name mismatch: got %s ; expected %t", tt.tr.GetPipelineRunPVCName(), tt.want) + } + }) + } +} + +func TestTaskRunIsDone(t *testing.T) { + tr := &v1.TaskRun{ + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + }}, + }, + }, + } + if !tr.IsDone() { + t.Fatal("Expected pipelinerun status to be done") + } +} + +func TestTaskRunIsCancelled(t *testing.T) { + tr := &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + Status: v1.TaskRunSpecStatusCancelled, + }, + } + if !tr.IsCancelled() { + t.Fatal("Expected pipelinerun status to be cancelled") + } + expected := "" + if string(tr.Spec.StatusMessage) != expected { + t.Fatalf("Expected StatusMessage is %s but got %s", expected, tr.Spec.StatusMessage) + } +} + +func TestTaskRunIsCancelledWithMessage(t *testing.T) { + expectedStatusMessage := "test message" + tr := &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + Status: v1.TaskRunSpecStatusCancelled, + StatusMessage: v1.TaskRunSpecStatusMessage(expectedStatusMessage), + }, + } + if !tr.IsCancelled() { + t.Fatal("Expected pipelinerun status to be cancelled") + } + + if string(tr.Spec.StatusMessage) != expectedStatusMessage { + t.Fatalf("Expected StatusMessage is %s but got %s", v1.TaskRunCancelledByPipelineMsg, tr.Spec.StatusMessage) + } +} + +func TestTaskRunHasVolumeClaimTemplate(t *testing.T) { + tr := &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + Workspaces: []v1.WorkspaceBinding{{ + Name: "my-workspace", + VolumeClaimTemplate: &corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pvc", + }, + Spec: corev1.PersistentVolumeClaimSpec{}, + }, + }}, + }, + } + if !tr.HasVolumeClaimTemplate() { + t.Fatal("Expected taskrun to have a volumeClaimTemplate workspace") + } +} + +func TestTaskRunKey(t *testing.T) { + tr := &v1.TaskRun{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "trunname"}} + n := tr.GetNamespacedName() + expected := "foo/trunname" + if n.String() != expected { + t.Fatalf("Expected name to be %s but got %s", expected, n.String()) + } +} + +func TestTaskRunHasStarted(t *testing.T) { + params := []struct { + name string + trStatus v1.TaskRunStatus + expectedValue bool + }{{ + name: "trWithNoStartTime", + trStatus: v1.TaskRunStatus{}, + expectedValue: false, + }, { + name: "trWithStartTime", + trStatus: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: now}, + }, + }, + expectedValue: true, + }, { + name: "trWithZeroStartTime", + trStatus: v1.TaskRunStatus{ + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{}, + }, + }, + expectedValue: false, + }} + for _, tc := range params { + t.Run(tc.name, func(t *testing.T) { + tr := &v1.TaskRun{} + tr.Status = tc.trStatus + if tr.HasStarted() != tc.expectedValue { + t.Fatalf("Expected taskrun HasStarted() to return %t but got %t", tc.expectedValue, tr.HasStarted()) + } + }) + } +} + +func TestTaskRunIsOfPipelinerun(t *testing.T) { + tests := []struct { + name string + tr *v1.TaskRun + expectedValue bool + expetectedPipeline string + expetectedPipelineRun string + }{{ + name: "yes", + tr: &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + pipeline.PipelineLabelKey: "pipeline", + pipeline.PipelineRunLabelKey: "pipelinerun", + }, + }, + }, + expectedValue: true, + expetectedPipeline: "pipeline", + expetectedPipelineRun: "pipelinerun", + }, { + name: "no", + tr: &v1.TaskRun{}, + expectedValue: false, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + value, pipeline, pipelineRun := test.tr.IsPartOfPipeline() + if value != test.expectedValue { + t.Fatalf("Expecting %v got %v", test.expectedValue, value) + } + + if pipeline != test.expetectedPipeline { + t.Fatalf("Mismatch in pipeline: got %s expected %s", pipeline, test.expetectedPipeline) + } + + if pipelineRun != test.expetectedPipelineRun { + t.Fatalf("Mismatch in pipelinerun: got %s expected %s", pipelineRun, test.expetectedPipelineRun) + } + }) + } +} + +func TestHasTimedOut(t *testing.T) { + // IsZero reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC + zeroTime := time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC) + testCases := []struct { + name string + taskRun *v1.TaskRun + expectedStatus bool + }{{ + name: "TaskRun not started", + taskRun: &v1.TaskRun{ + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + }}, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: zeroTime}, + }, + }, + }, + expectedStatus: false, + }, { + name: "TaskRun no timeout", + taskRun: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + Timeout: &metav1.Duration{ + Duration: 0 * time.Minute, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + }}, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: now.Add(-15 * time.Hour)}, + }, + }, + }, + expectedStatus: false, + }, { + name: "TaskRun timed out", + taskRun: &v1.TaskRun{ + Spec: v1.TaskRunSpec{ + Timeout: &metav1.Duration{ + Duration: 10 * time.Second, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: []apis.Condition{{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + }}, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: now.Add(-15 * time.Second)}, + }, + }, + }, + expectedStatus: true, + }} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.taskRun.HasTimedOut(context.Background(), testClock) + if d := cmp.Diff(result, tc.expectedStatus); d != "" { + t.Fatalf(diff.PrintWantGot(d)) + } + }) + } +} + +func TestInitializeTaskRunConditions(t *testing.T) { + tr := &v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-name", + Namespace: "test-ns", + }, + } + tr.Status.InitializeConditions() + + if tr.Status.StartTime.IsZero() { + t.Fatalf("TaskRun StartTime not initialized correctly") + } + + condition := tr.Status.GetCondition(apis.ConditionSucceeded) + if condition.Reason != v1.TaskRunReasonStarted.String() { + t.Fatalf("TaskRun initialize reason should be %s, got %s instead", v1.TaskRunReasonStarted.String(), condition.Reason) + } + + // Change the reason before we initialize again + tr.Status.SetCondition(&apis.Condition{ + Type: apis.ConditionSucceeded, + Status: corev1.ConditionUnknown, + Reason: "not just started", + Message: "hello", + }) + + tr.Status.InitializeConditions() + + newCondition := tr.Status.GetCondition(apis.ConditionSucceeded) + if newCondition.Reason != "not just started" { + t.Fatalf("PipelineRun initialize reset the condition reason to %s", newCondition.Reason) + } +} diff --git a/pkg/apis/pipeline/v1/taskrun_validation.go b/pkg/apis/pipeline/v1/taskrun_validation.go new file mode 100644 index 00000000000..a02f3db1dd4 --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_validation.go @@ -0,0 +1,236 @@ +/* +Copyright 2022 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 v1 + +import ( + "context" + "fmt" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "github.com/tektoncd/pipeline/pkg/apis/version" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*TaskRun)(nil) + +// Validate taskrun +func (tr *TaskRun) Validate(ctx context.Context) *apis.FieldError { + if apis.IsInDelete(ctx) { + return nil + } + errs := validate.ObjectMetadata(tr.GetObjectMeta()).ViaField("metadata") + return errs.Also(tr.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec")) +} + +// Validate taskrun spec +func (ts *TaskRunSpec) Validate(ctx context.Context) (errs *apis.FieldError) { + // Must have exactly one of taskRef and taskSpec. + if ts.TaskRef == nil && ts.TaskSpec == nil { + errs = errs.Also(apis.ErrMissingOneOf("taskRef", "taskSpec")) + } + if ts.TaskRef != nil && ts.TaskSpec != nil { + errs = errs.Also(apis.ErrMultipleOneOf("taskRef", "taskSpec")) + } + // Validate TaskRef if it's present. + if ts.TaskRef != nil { + errs = errs.Also(ts.TaskRef.Validate(ctx).ViaField("taskRef")) + } + // Validate TaskSpec if it's present. + if ts.TaskSpec != nil { + // skip validation of parameter and workspaces variables since we validate them via taskrunspec below. + ctx = config.SkipValidationDueToPropagatedParametersAndWorkspaces(ctx, true) + errs = errs.Also(ts.TaskSpec.Validate(ctx).ViaField("taskSpec")) + } + + errs = errs.Also(ValidateParameters(ctx, ts.Params).ViaField("params")) + + // Validate propagated parameters + errs = errs.Also(ts.validateInlineParameters(ctx)) + errs = errs.Also(ValidateWorkspaceBindings(ctx, ts.Workspaces).ViaField("workspaces")) + if ts.Debug != nil { + errs = errs.Also(version.ValidateEnabledAPIFields(ctx, "debug", config.AlphaAPIFields).ViaField("debug")) + errs = errs.Also(validateDebug(ts.Debug).ViaField("debug")) + } + if ts.StepOverrides != nil { + errs = errs.Also(version.ValidateEnabledAPIFields(ctx, "stepOverrides", config.AlphaAPIFields).ViaField("stepOverrides")) + errs = errs.Also(validateStepOverrides(ts.StepOverrides).ViaField("stepOverrides")) + } + if ts.SidecarOverrides != nil { + errs = errs.Also(version.ValidateEnabledAPIFields(ctx, "sidecarOverrides", config.AlphaAPIFields).ViaField("sidecarOverrides")) + errs = errs.Also(validateSidecarOverrides(ts.SidecarOverrides).ViaField("sidecarOverrides")) + } + if ts.ComputeResources != nil { + errs = errs.Also(version.ValidateEnabledAPIFields(ctx, "computeResources", config.AlphaAPIFields).ViaField("computeResources")) + errs = errs.Also(validateTaskRunComputeResources(ts.ComputeResources, ts.StepOverrides)) + } + + if ts.Status != "" { + if ts.Status != TaskRunSpecStatusCancelled { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be %s", ts.Status, TaskRunSpecStatusCancelled), "status")) + } + } + if ts.Status == "" { + if ts.StatusMessage != "" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("statusMessage should not be set if status is not set, but it is currently set to %s", ts.StatusMessage), "statusMessage")) + } + } + + if ts.Timeout != nil { + // timeout should be a valid duration of at least 0. + if ts.Timeout.Duration < 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be >= 0", ts.Timeout.Duration.String()), "timeout")) + } + } + + return errs +} + +// validateInlineParameters validates that any parameters called in the +// Task spec are declared in the TaskRun. +// This is crucial for propagated parameters because the parameters could +// be defined under taskRun and then called directly in the task steps. +// In this case, parameters cannot be validated by the underlying taskSpec +// since they may not have the parameters declared because of propagation. +func (ts *TaskRunSpec) validateInlineParameters(ctx context.Context) (errs *apis.FieldError) { + if ts.TaskSpec == nil { + return errs + } + var paramSpec []ParamSpec + for _, p := range ts.Params { + pSpec := ParamSpec{ + Name: p.Name, + Default: &p.Value, + } + paramSpec = append(paramSpec, pSpec) + } + for _, p := range ts.TaskSpec.Params { + skip := false + for _, ps := range paramSpec { + if ps.Name == p.Name { + skip = true + break + } + } + if !skip { + paramSpec = append(paramSpec, p) + } + } + if ts.TaskSpec != nil && ts.TaskSpec.Steps != nil { + errs = errs.Also(ValidateParameterVariables(config.SkipValidationDueToPropagatedParametersAndWorkspaces(ctx, false), ts.TaskSpec.Steps, paramSpec)) + } + return errs +} + +// validateDebug +func validateDebug(db *TaskRunDebug) (errs *apis.FieldError) { + breakpointOnFailure := "onFailure" + validBreakpoints := sets.NewString() + validBreakpoints.Insert(breakpointOnFailure) + + for _, b := range db.Breakpoint { + if !validBreakpoints.Has(b) { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s is not a valid breakpoint. Available valid breakpoints include %s", b, validBreakpoints.List()), "breakpoint")) + } + } + return errs +} + +// ValidateWorkspaceBindings makes sure the volumes provided for the Task's declared workspaces make sense. +func ValidateWorkspaceBindings(ctx context.Context, wb []WorkspaceBinding) (errs *apis.FieldError) { + var names []string + for idx, w := range wb { + names = append(names, w.Name) + errs = errs.Also(w.Validate(ctx).ViaIndex(idx)) + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +// ValidateParameters makes sure the params for the Task are valid. +func ValidateParameters(ctx context.Context, params []Param) (errs *apis.FieldError) { + var names []string + for _, p := range params { + if p.Value.Type == ParamTypeObject { + // Object type parameter is an alpha feature and will fail validation if it's used in a taskrun spec + // when the enable-api-fields feature gate is not "alpha". + errs = errs.Also(version.ValidateEnabledAPIFields(ctx, "object type parameter", config.AlphaAPIFields)) + } + names = append(names, p.Name) + } + return errs.Also(validateNoDuplicateNames(names, false)) +} + +func validateStepOverrides(overrides []TaskRunStepOverride) (errs *apis.FieldError) { + var names []string + for i, o := range overrides { + if o.Name == "" { + errs = errs.Also(apis.ErrMissingField("name").ViaIndex(i)) + } else { + names = append(names, o.Name) + } + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +// validateTaskRunComputeResources ensures that compute resources are not configured at both the step level and the task level +func validateTaskRunComputeResources(computeResources *corev1.ResourceRequirements, overrides []TaskRunStepOverride) (errs *apis.FieldError) { + for _, override := range overrides { + if override.Resources.Size() != 0 && computeResources != nil { + return apis.ErrMultipleOneOf( + "stepOverrides.resources", + "computeResources", + ) + } + } + return nil +} + +func validateSidecarOverrides(overrides []TaskRunSidecarOverride) (errs *apis.FieldError) { + var names []string + for i, o := range overrides { + if o.Name == "" { + errs = errs.Also(apis.ErrMissingField("name").ViaIndex(i)) + } else { + names = append(names, o.Name) + } + } + errs = errs.Also(validateNoDuplicateNames(names, true)) + return errs +} + +// validateNoDuplicateNames returns an error for each name that is repeated in names. +// Case insensitive. +// If byIndex is true, the error will be reported by index instead of by key. +func validateNoDuplicateNames(names []string, byIndex bool) (errs *apis.FieldError) { + seen := sets.NewString() + for i, n := range names { + if seen.Has(strings.ToLower(n)) { + if byIndex { + errs = errs.Also(apis.ErrMultipleOneOf("name").ViaIndex(i)) + } else { + errs = errs.Also(apis.ErrMultipleOneOf("name").ViaKey(n)) + } + } + seen.Insert(strings.ToLower(n)) + } + return errs +} diff --git a/pkg/apis/pipeline/v1/taskrun_validation_test.go b/pkg/apis/pipeline/v1/taskrun_validation_test.go new file mode 100644 index 00000000000..8b2cc82643c --- /dev/null +++ b/pkg/apis/pipeline/v1/taskrun_validation_test.go @@ -0,0 +1,887 @@ +/* +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 v1_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1" + "github.com/tektoncd/pipeline/test/diff" + corev1 "k8s.io/api/core/v1" + corev1resources "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +func TestTaskRun_Invalidate(t *testing.T) { + tests := []struct { + name string + taskRun *v1beta1.TaskRun + want *apis.FieldError + wc func(context.Context) context.Context + }{{ + name: "invalid taskspec", + taskRun: &v1beta1.TaskRun{}, + want: apis.ErrMissingOneOf("spec.taskRef", "spec.taskSpec").Also( + apis.ErrGeneric(`invalid resource name "": must be a valid DNS label`, "metadata.name")), + }, { + name: "propagating params not provided but used by step", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "echo", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words[*])"}, + }}, + }, + }, + }, + want: &apis.FieldError{ + Message: `non-existent variable in "$(params.task-words[*])"`, + Paths: []string{"spec.steps[0].args[0]"}, + }, + wc: config.EnableAlphaAPIFields, + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + err := ts.taskRun.Validate(ctx) + if d := cmp.Diff(ts.want.Error(), err.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} + +func TestTaskRun_Validate(t *testing.T) { + tests := []struct { + name string + taskRun *v1beta1.TaskRun + wc func(context.Context) context.Context + }{{ + name: "do not validate spec on delete", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "taskrname"}, + }, + wc: apis.WithinDelete, + }, { + name: "propagating params with taskrun", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "task-words", + Value: v1beta1.ParamValue{ + ArrayVal: []string{"hello", "task run"}, + }, + }}, + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "echo", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words[*])"}, + }}, + }, + }, + }, + wc: config.EnableAlphaAPIFields, + }, { + name: "propagating partial params with different provided and default names", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "task-words", + Value: v1beta1.ParamValue{ + ArrayVal: []string{"hello", "task run"}, + }, + }}, + TaskSpec: &v1beta1.TaskSpec{ + Params: []v1beta1.ParamSpec{{ + Name: "task-words-2", + Type: v1beta1.ParamTypeArray, + }}, + Steps: []v1beta1.Step{{ + Name: "task-echo", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words[*])"}, + }, { + Name: "task-echo-2", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words-2[*])"}, + }}, + }, + }, + }, + wc: config.EnableAlphaAPIFields, + }, { + name: "propagating partial params in taskrun", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "task-words", + Value: v1beta1.ParamValue{ + ArrayVal: []string{"hello", "task run"}, + }, + }}, + TaskSpec: &v1beta1.TaskSpec{ + Params: []v1beta1.ParamSpec{{ + Name: "task-words-2", + Type: v1beta1.ParamTypeArray, + }, { + Name: "task-words", + Type: v1beta1.ParamTypeArray, + }}, + Steps: []v1beta1.Step{{ + Name: "echo", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words[*])"}, + }, { + Name: "echo-2", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words-2[*])"}, + }}, + }, + }, + }, + wc: config.EnableAlphaAPIFields, + }, { + name: "propagating params with taskrun same names", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "task-words", + Value: v1beta1.ParamValue{ + ArrayVal: []string{"hello", "task run"}, + }, + }}, + TaskSpec: &v1beta1.TaskSpec{ + Params: []v1beta1.ParamSpec{{ + Name: "task-words", + Type: v1beta1.ParamTypeArray, + }}, + Steps: []v1beta1.Step{{ + Name: "echo", + Image: "ubuntu", + Command: []string{"echo"}, + Args: []string{"$(params.task-words[*])"}, + }}, + }, + }, + }, + wc: config.EnableAlphaAPIFields, + }, { + name: "alpha feature: valid step and sidecar overrides", + taskRun: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "tr"}, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + StepOverrides: []v1beta1.TaskRunStepOverride{{ + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + SidecarOverrides: []v1beta1.TaskRunSidecarOverride{{ + Name: "bar", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + }, + wc: config.EnableAlphaAPIFields, + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + if err := ts.taskRun.Validate(ctx); err != nil { + t.Errorf("TaskRun.Validate() error = %v", err) + } + }) + } +} + +func TestTaskRun_Workspaces_Invalid(t *testing.T) { + tests := []struct { + name string + tr *v1beta1.TaskRun + wantErr *apis.FieldError + }{{ + name: "make sure WorkspaceBinding validation invoked", + tr: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "taskname"}, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + Workspaces: []v1beta1.WorkspaceBinding{{ + Name: "workspace", + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{}, + }}, + }, + }, + wantErr: apis.ErrMissingField("spec.workspaces[0].persistentvolumeclaim.claimname"), + }, { + name: "bind same workspace twice", + tr: &v1beta1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{Name: "taskname"}, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + Workspaces: []v1beta1.WorkspaceBinding{{ + Name: "workspace", + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, { + Name: "workspace", + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }}, + }, + }, + wantErr: apis.ErrMultipleOneOf("spec.workspaces[1].name"), + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + err := ts.tr.Validate(context.Background()) + if err == nil { + t.Errorf("Expected error for invalid TaskRun but got none") + } + if d := cmp.Diff(ts.wantErr.Error(), err.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} + +func TestTaskRunSpec_Invalidate(t *testing.T) { + invalidStatusMessage := "status message without status" + tests := []struct { + name string + spec v1beta1.TaskRunSpec + wantErr *apis.FieldError + wc func(context.Context) context.Context + }{{ + name: "invalid taskspec", + spec: v1beta1.TaskRunSpec{}, + wantErr: apis.ErrMissingOneOf("taskRef", "taskSpec"), + }, { + name: "invalid taskref and taskspec together", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "taskrefname", + }, + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "mystep", + Image: "myimage", + }}, + }, + }, + wantErr: apis.ErrMultipleOneOf("taskRef", "taskSpec"), + }, { + name: "negative pipeline timeout", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "taskrefname", + }, + Timeout: &metav1.Duration{Duration: -48 * time.Hour}, + }, + wantErr: apis.ErrInvalidValue("-48h0m0s should be >= 0", "timeout"), + }, { + name: "wrong taskrun cancel", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "taskrefname", + }, + Status: "TaskRunCancell", + }, + wantErr: apis.ErrInvalidValue("TaskRunCancell should be TaskRunCancelled", "status"), + }, { + name: "incorrectly set statusMesage", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "taskrefname", + }, + StatusMessage: v1beta1.TaskRunSpecStatusMessage(invalidStatusMessage), + }, + wantErr: apis.ErrInvalidValue(fmt.Sprintf("statusMessage should not be set if status is not set, but it is currently set to %s", invalidStatusMessage), "statusMessage"), + }, { + name: "invalid taskspec", + spec: v1beta1.TaskRunSpec{ + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "invalid-name-with-$weird-char/%", + Image: "myimage", + }}, + }, + }, + wantErr: &apis.FieldError{ + Message: `invalid value "invalid-name-with-$weird-char/%"`, + Paths: []string{"taskSpec.steps[0].name"}, + Details: "Task step name must be a valid DNS Label, For more info refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", + }, + }, { + name: "invalid params - exactly same names", + spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "myname", + Value: *v1beta1.NewStructuredValues("value"), + }, { + Name: "myname", + Value: *v1beta1.NewStructuredValues("value"), + }}, + TaskRef: &v1beta1.TaskRef{Name: "mytask"}, + }, + wantErr: apis.ErrMultipleOneOf("params[myname].name"), + }, { + name: "invalid params - same names but different case", + spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "FOO", + Value: *v1beta1.NewStructuredValues("value"), + }, { + Name: "foo", + Value: *v1beta1.NewStructuredValues("value"), + }}, + TaskRef: &v1beta1.TaskRef{Name: "mytask"}, + }, + wantErr: apis.ErrMultipleOneOf("params[foo].name"), + }, { + name: "invalid params (object type) - same names but different case", + spec: v1beta1.TaskRunSpec{ + Params: []v1beta1.Param{{ + Name: "MYOBJECTPARAM", + Value: *v1beta1.NewObject(map[string]string{"key1": "val1", "key2": "val2"}), + }, { + Name: "myobjectparam", + Value: *v1beta1.NewObject(map[string]string{"key1": "val1", "key2": "val2"}), + }}, + TaskRef: &v1beta1.TaskRef{Name: "mytask"}, + }, + wantErr: apis.ErrMultipleOneOf("params[myobjectparam].name"), + wc: config.EnableAlphaAPIFields, + }, { + name: "using debug when apifields stable", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "my-task", + }, + Debug: &v1beta1.TaskRunDebug{ + Breakpoint: []string{"onFailure"}, + }, + }, + wantErr: apis.ErrGeneric("debug requires \"enable-api-fields\" feature gate to be \"alpha\" but it is \"stable\""), + }, { + name: "invalid breakpoint", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "my-task", + }, + Debug: &v1beta1.TaskRunDebug{ + Breakpoint: []string{"breakito"}, + }, + }, + wantErr: apis.ErrInvalidValue("breakito is not a valid breakpoint. Available valid breakpoints include [onFailure]", "debug.breakpoint"), + wc: config.EnableAlphaAPIFields, + }, { + name: "stepOverride disallowed without alpha feature gate", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "foo", + }, + StepOverrides: []v1beta1.TaskRunStepOverride{{ + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrGeneric("stepOverrides requires \"enable-api-fields\" feature gate to be \"alpha\" but it is \"stable\""), + }, { + name: "sidecarOverride disallowed without alpha feature gate", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "foo", + }, + SidecarOverrides: []v1beta1.TaskRunSidecarOverride{{ + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrGeneric("sidecarOverrides requires \"enable-api-fields\" feature gate to be \"alpha\" but it is \"stable\""), + }, { + name: "duplicate stepOverride names", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + StepOverrides: []v1beta1.TaskRunStepOverride{{ + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }, { + Name: "foo", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrMultipleOneOf("stepOverrides[1].name"), + wc: config.EnableAlphaAPIFields, + }, { + name: "missing stepOverride names", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + StepOverrides: []v1beta1.TaskRunStepOverride{{ + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrMissingField("stepOverrides[0].name"), + wc: config.EnableAlphaAPIFields, + }, { + name: "duplicate sidecarOverride names", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + SidecarOverrides: []v1beta1.TaskRunSidecarOverride{{ + Name: "bar", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }, { + Name: "bar", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrMultipleOneOf("sidecarOverrides[1].name"), + wc: config.EnableAlphaAPIFields, + }, { + name: "missing sidecarOverride names", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + SidecarOverrides: []v1beta1.TaskRunSidecarOverride{{ + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{corev1.ResourceMemory: corev1resources.MustParse("1Gi")}, + }, + }}, + }, + wantErr: apis.ErrMissingField("sidecarOverrides[0].name"), + wc: config.EnableAlphaAPIFields, + }, { + name: "invalid both step-level (stepOverrides.resources) and task-level (spec.computeResources) resource requirements", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + StepOverrides: []v1beta1.TaskRunStepOverride{{ + Name: "stepOverride", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("1Gi"), + }, + }, + }}, + ComputeResources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("2Gi"), + }, + }, + }, + wantErr: apis.ErrMultipleOneOf( + "stepOverrides.resources", + "computeResources", + ), + wc: config.EnableAlphaAPIFields, + }, { + name: "computeResources disallowed without alpha feature gate", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "foo", + }, + ComputeResources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("2Gi"), + }, + }, + }, + wantErr: apis.ErrGeneric("computeResources requires \"enable-api-fields\" feature gate to be \"alpha\" but it is \"stable\""), + }} + + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + err := ts.spec.Validate(ctx) + if d := cmp.Diff(ts.wantErr.Error(), err.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} + +func TestTaskRunSpec_Validate(t *testing.T) { + tests := []struct { + name string + spec v1beta1.TaskRunSpec + wc func(context.Context) context.Context + }{{ + name: "taskspec without a taskRef", + spec: v1beta1.TaskRunSpec{ + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "mystep", + Image: "myimage", + }}, + }, + }, + }, { + name: "no timeout", + spec: v1beta1.TaskRunSpec{ + Timeout: &metav1.Duration{Duration: 0}, + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "mystep", + Image: "myimage", + }}, + }, + }, + }, { + name: "parameters", + spec: v1beta1.TaskRunSpec{ + Timeout: &metav1.Duration{Duration: 0}, + Params: []v1beta1.Param{{ + Name: "name", + Value: *v1beta1.NewStructuredValues("value"), + }}, + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "mystep", + Image: "myimage", + }}, + }, + }, + }, { + name: "task spec with credentials.path variable", + spec: v1beta1.TaskRunSpec{ + TaskSpec: &v1beta1.TaskSpec{ + Steps: []v1beta1.Step{{ + Name: "mystep", + Image: "myimage", + Script: `echo "creds-init writes to $(credentials.path)"`, + }}, + }, + }, + }, { + name: "valid task-level (spec.resources) resource requirements", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + ComputeResources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("2Gi"), + }, + }, + }, + wc: config.EnableAlphaAPIFields, + }, { + name: "valid sidecar and task-level (spec.resources) resource requirements", + spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{Name: "task"}, + ComputeResources: &corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("2Gi"), + }, + }, + SidecarOverrides: []v1beta1.TaskRunSidecarOverride{{ + Name: "sidecar", + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: corev1resources.MustParse("4Gi"), + }, + }, + }}, + }, + wc: config.EnableAlphaAPIFields, + }} + + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + if err := ts.spec.Validate(ctx); err != nil { + t.Error(err) + } + }) + } +} + +func TestResources_Validate(t *testing.T) { + tests := []struct { + name string + resources *v1beta1.TaskRunResources + }{{ + name: "no resources is valid", + }, { + name: "inputs only", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + Name: "workspace", + }, + }}, + }, + }, { + name: "multiple inputs only", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource1", + }, + Name: "workspace1", + }, + }, { + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource2", + }, + Name: "workspace2", + }, + }}, + }, + }, { + name: "outputs only", + resources: &v1beta1.TaskRunResources{ + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + Name: "workspace", + }, + }}, + }, + }, { + name: "multiple outputs only", + resources: &v1beta1.TaskRunResources{ + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource1", + }, + Name: "workspace1", + }, + }, { + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource2", + }, + Name: "workspace2", + }, + }}, + }, + }, { + name: "inputs and outputs", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + Name: "workspace", + }, + }}, + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + Name: "workspace", + }, + }}, + }, + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + if err := ts.resources.Validate(context.Background()); err != nil { + t.Errorf("TaskRunInputs.Validate() error = %v", err) + } + }) + } + +} + +func TestResources_Invalidate(t *testing.T) { + tests := []struct { + name string + resources *v1beta1.TaskRunResources + wantErr *apis.FieldError + }{{ + name: "duplicate task inputs", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource1", + }, + Name: "workspace", + }, + }, { + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource2", + }, + Name: "workspace", + }, + }}, + }, + wantErr: apis.ErrMultipleOneOf("spec.resources.inputs.name"), + }, { + name: "duplicate resource ref and resource spec", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + ResourceSpec: &resource.PipelineResourceSpec{ + Type: v1beta1.PipelineResourceTypeGit, + }, + Name: "resource-dup", + }, + }}, + }, + wantErr: apis.ErrDisallowedFields("spec.resources.inputs.name.resourceRef", "spec.resources.inputs.name.resourceSpec"), + }, { + name: "invalid resource spec", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceSpec: &resource.PipelineResourceSpec{ + Type: "non-existent", + }, + Name: "resource-inv", + }, + }}, + }, + wantErr: apis.ErrInvalidValue("spec.type", "non-existent"), + }, { + name: "no resource ref", // and resource spec + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + Name: "resource", + }, + }}, + }, + wantErr: apis.ErrMissingField("spec.resources.inputs.name.resourceRef", "spec.resources.inputs.name.resourceSpec"), + }, { + name: "duplicate task outputs", + resources: &v1beta1.TaskRunResources{ + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource1", + }, + Name: "workspace", + }, + }, { + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource2", + }, + Name: "workspace", + }, + }}, + }, + wantErr: apis.ErrMultipleOneOf("spec.resources.outputs.name"), + }, { + name: "duplicate resource ref and resource spec", + resources: &v1beta1.TaskRunResources{ + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceRef: &v1beta1.PipelineResourceRef{ + Name: "testresource", + }, + ResourceSpec: &resource.PipelineResourceSpec{ + Type: v1beta1.PipelineResourceTypeGit, + }, + Name: "resource-dup", + }, + }}, + }, + wantErr: apis.ErrDisallowedFields("spec.resources.outputs.name.resourceRef", "spec.resources.outputs.name.resourceSpec"), + }, { + name: "invalid resource spec", + resources: &v1beta1.TaskRunResources{ + Inputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + ResourceSpec: &resource.PipelineResourceSpec{ + Type: "non-existent", + }, + Name: "resource-inv", + }, + }}, + }, + wantErr: apis.ErrInvalidValue("spec.type", "non-existent"), + }, { + name: "no resource ref ", // and resource spec + resources: &v1beta1.TaskRunResources{ + Outputs: []v1beta1.TaskResourceBinding{{ + PipelineResourceBinding: v1beta1.PipelineResourceBinding{ + Name: "resource", + }, + }}, + }, + wantErr: apis.ErrMissingField("spec.resources.outputs.name.resourceRef", "spec.resources.outputs.name.resourceSpec"), + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + err := ts.resources.Validate(context.Background()) + if d := cmp.Diff(err.Error(), ts.wantErr.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/apis/pipeline/v1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1/zz_generated.deepcopy.go index 17e15a729a1..e7df0254eb8 100644 --- a/pkg/apis/pipeline/v1/zz_generated.deepcopy.go +++ b/pkg/apis/pipeline/v1/zz_generated.deepcopy.go @@ -22,6 +22,7 @@ limitations under the License. package v1 import ( + pod "github.com/tektoncd/pipeline/pkg/apis/pipeline/pod" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -549,6 +550,23 @@ func (in *Sidecar) DeepCopy() *Sidecar { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SidecarState) DeepCopyInto(out *SidecarState) { + *out = *in + in.ContainerState.DeepCopyInto(&out.ContainerState) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SidecarState. +func (in *SidecarState) DeepCopy() *SidecarState { + if in == nil { + return nil + } + out := new(SidecarState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Step) DeepCopyInto(out *Step) { *out = *in @@ -643,6 +661,23 @@ func (in *StepOutputConfig) DeepCopy() *StepOutputConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StepState) DeepCopyInto(out *StepState) { + *out = *in + in.ContainerState.DeepCopyInto(&out.ContainerState) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StepState. +func (in *StepState) DeepCopy() *StepState { + if in == nil { + return nil + } + out := new(StepState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StepTemplate) DeepCopyInto(out *StepTemplate) { *out = *in @@ -801,6 +836,111 @@ func (in *TaskResult) DeepCopy() *TaskResult { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRun) DeepCopyInto(out *TaskRun) { + *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 TaskRun. +func (in *TaskRun) DeepCopy() *TaskRun { + if in == nil { + return nil + } + out := new(TaskRun) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRun) 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 *TaskRunDebug) DeepCopyInto(out *TaskRunDebug) { + *out = *in + if in.Breakpoint != nil { + in, out := &in.Breakpoint, &out.Breakpoint + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunDebug. +func (in *TaskRunDebug) DeepCopy() *TaskRunDebug { + if in == nil { + return nil + } + out := new(TaskRunDebug) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunInputs) DeepCopyInto(out *TaskRunInputs) { + *out = *in + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]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 TaskRunInputs. +func (in *TaskRunInputs) DeepCopy() *TaskRunInputs { + if in == nil { + return nil + } + out := new(TaskRunInputs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunList) DeepCopyInto(out *TaskRunList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]TaskRun, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunList. +func (in *TaskRunList) DeepCopy() *TaskRunList { + if in == nil { + return nil + } + out := new(TaskRunList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *TaskRunList) 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 *TaskRunResult) DeepCopyInto(out *TaskRunResult) { *out = *in @@ -818,6 +958,189 @@ func (in *TaskRunResult) DeepCopy() *TaskRunResult { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunSidecarOverride) DeepCopyInto(out *TaskRunSidecarOverride) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunSidecarOverride. +func (in *TaskRunSidecarOverride) DeepCopy() *TaskRunSidecarOverride { + if in == nil { + return nil + } + out := new(TaskRunSidecarOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunSpec) DeepCopyInto(out *TaskRunSpec) { + *out = *in + if in.Debug != nil { + in, out := &in.Debug, &out.Debug + *out = new(TaskRunDebug) + (*in).DeepCopyInto(*out) + } + if in.Params != nil { + in, out := &in.Params, &out.Params + *out = make([]Param, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskRef != nil { + in, out := &in.TaskRef, &out.TaskRef + *out = new(TaskRef) + (*in).DeepCopyInto(*out) + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(metav1.Duration) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(pod.Template) + (*in).DeepCopyInto(*out) + } + if in.Workspaces != nil { + in, out := &in.Workspaces, &out.Workspaces + *out = make([]WorkspaceBinding, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.StepOverrides != nil { + in, out := &in.StepOverrides, &out.StepOverrides + *out = make([]TaskRunStepOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.SidecarOverrides != nil { + in, out := &in.SidecarOverrides, &out.SidecarOverrides + *out = make([]TaskRunSidecarOverride, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ComputeResources != nil { + in, out := &in.ComputeResources, &out.ComputeResources + *out = new(corev1.ResourceRequirements) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunSpec. +func (in *TaskRunSpec) DeepCopy() *TaskRunSpec { + if in == nil { + return nil + } + out := new(TaskRunSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStatus) DeepCopyInto(out *TaskRunStatus) { + *out = *in + in.Status.DeepCopyInto(&out.Status) + in.TaskRunStatusFields.DeepCopyInto(&out.TaskRunStatusFields) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStatus. +func (in *TaskRunStatus) DeepCopy() *TaskRunStatus { + if in == nil { + return nil + } + out := new(TaskRunStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStatusFields) DeepCopyInto(out *TaskRunStatusFields) { + *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.Steps != nil { + in, out := &in.Steps, &out.Steps + *out = make([]StepState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RetriesStatus != nil { + in, out := &in.RetriesStatus, &out.RetriesStatus + *out = make([]TaskRunStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Results != nil { + in, out := &in.Results, &out.Results + *out = make([]TaskRunResult, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sidecars != nil { + in, out := &in.Sidecars, &out.Sidecars + *out = make([]SidecarState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.TaskSpec != nil { + in, out := &in.TaskSpec, &out.TaskSpec + *out = new(TaskSpec) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStatusFields. +func (in *TaskRunStatusFields) DeepCopy() *TaskRunStatusFields { + if in == nil { + return nil + } + out := new(TaskRunStatusFields) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TaskRunStepOverride) DeepCopyInto(out *TaskRunStepOverride) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TaskRunStepOverride. +func (in *TaskRunStepOverride) DeepCopy() *TaskRunStepOverride { + if in == nil { + return nil + } + out := new(TaskRunStepOverride) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TaskSpec) DeepCopyInto(out *TaskSpec) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_pipeline_client.go b/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_pipeline_client.go index e84bdce7ff8..87ade292418 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_pipeline_client.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_pipeline_client.go @@ -36,6 +36,10 @@ func (c *FakeTektonV1) Tasks(namespace string) v1.TaskInterface { return &FakeTasks{c, namespace} } +func (c *FakeTektonV1) TaskRuns(namespace string) v1.TaskRunInterface { + return &FakeTaskRuns{c, namespace} +} + // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. func (c *FakeTektonV1) RESTClient() rest.Interface { diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_taskrun.go b/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_taskrun.go new file mode 100644 index 00000000000..e5b61a4b124 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1/fake/fake_taskrun.go @@ -0,0 +1,142 @@ +/* +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 ( + "context" + + pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + 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" +) + +// FakeTaskRuns implements TaskRunInterface +type FakeTaskRuns struct { + Fake *FakeTektonV1 + ns string +} + +var taskrunsResource = schema.GroupVersionResource{Group: "tekton.dev", Version: "v1", Resource: "taskruns"} + +var taskrunsKind = schema.GroupVersionKind{Group: "tekton.dev", Version: "v1", Kind: "TaskRun"} + +// Get takes name of the taskRun, and returns the corresponding taskRun object, and an error if there is any. +func (c *FakeTaskRuns) Get(ctx context.Context, name string, options v1.GetOptions) (result *pipelinev1.TaskRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(taskrunsResource, c.ns, name), &pipelinev1.TaskRun{}) + + if obj == nil { + return nil, err + } + return obj.(*pipelinev1.TaskRun), err +} + +// List takes label and field selectors, and returns the list of TaskRuns that match those selectors. +func (c *FakeTaskRuns) List(ctx context.Context, opts v1.ListOptions) (result *pipelinev1.TaskRunList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(taskrunsResource, taskrunsKind, c.ns, opts), &pipelinev1.TaskRunList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &pipelinev1.TaskRunList{ListMeta: obj.(*pipelinev1.TaskRunList).ListMeta} + for _, item := range obj.(*pipelinev1.TaskRunList).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 taskRuns. +func (c *FakeTaskRuns) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(taskrunsResource, c.ns, opts)) + +} + +// Create takes the representation of a taskRun and creates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *FakeTaskRuns) Create(ctx context.Context, taskRun *pipelinev1.TaskRun, opts v1.CreateOptions) (result *pipelinev1.TaskRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(taskrunsResource, c.ns, taskRun), &pipelinev1.TaskRun{}) + + if obj == nil { + return nil, err + } + return obj.(*pipelinev1.TaskRun), err +} + +// Update takes the representation of a taskRun and updates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *FakeTaskRuns) Update(ctx context.Context, taskRun *pipelinev1.TaskRun, opts v1.UpdateOptions) (result *pipelinev1.TaskRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(taskrunsResource, c.ns, taskRun), &pipelinev1.TaskRun{}) + + if obj == nil { + return nil, err + } + return obj.(*pipelinev1.TaskRun), 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 *FakeTaskRuns) UpdateStatus(ctx context.Context, taskRun *pipelinev1.TaskRun, opts v1.UpdateOptions) (*pipelinev1.TaskRun, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(taskrunsResource, "status", c.ns, taskRun), &pipelinev1.TaskRun{}) + + if obj == nil { + return nil, err + } + return obj.(*pipelinev1.TaskRun), err +} + +// Delete takes name of the taskRun and deletes it. Returns an error if one occurs. +func (c *FakeTaskRuns) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(taskrunsResource, c.ns, name, opts), &pipelinev1.TaskRun{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeTaskRuns) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(taskrunsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &pipelinev1.TaskRunList{}) + return err +} + +// Patch applies the patch and returns the patched taskRun. +func (c *FakeTaskRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *pipelinev1.TaskRun, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(taskrunsResource, c.ns, name, pt, data, subresources...), &pipelinev1.TaskRun{}) + + if obj == nil { + return nil, err + } + return obj.(*pipelinev1.TaskRun), err +} diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1/generated_expansion.go b/pkg/client/clientset/versioned/typed/pipeline/v1/generated_expansion.go index f14eaed9651..83baab3f218 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1/generated_expansion.go @@ -21,3 +21,5 @@ package v1 type PipelineExpansion interface{} type TaskExpansion interface{} + +type TaskRunExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1/pipeline_client.go b/pkg/client/clientset/versioned/typed/pipeline/v1/pipeline_client.go index 7e65d3314b1..68052ef8ccc 100644 --- a/pkg/client/clientset/versioned/typed/pipeline/v1/pipeline_client.go +++ b/pkg/client/clientset/versioned/typed/pipeline/v1/pipeline_client.go @@ -30,6 +30,7 @@ type TektonV1Interface interface { RESTClient() rest.Interface PipelinesGetter TasksGetter + TaskRunsGetter } // TektonV1Client is used to interact with features provided by the tekton.dev group. @@ -45,6 +46,10 @@ func (c *TektonV1Client) Tasks(namespace string) TaskInterface { return newTasks(c, namespace) } +func (c *TektonV1Client) TaskRuns(namespace string) TaskRunInterface { + return newTaskRuns(c, namespace) +} + // NewForConfig creates a new TektonV1Client for the given config. // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), // where httpClient was generated with rest.HTTPClientFor(c). diff --git a/pkg/client/clientset/versioned/typed/pipeline/v1/taskrun.go b/pkg/client/clientset/versioned/typed/pipeline/v1/taskrun.go new file mode 100644 index 00000000000..34145a97323 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pipeline/v1/taskrun.go @@ -0,0 +1,195 @@ +/* +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 v1 + +import ( + "context" + "time" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + scheme "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/scheme" + metav1 "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" +) + +// TaskRunsGetter has a method to return a TaskRunInterface. +// A group's client should implement this interface. +type TaskRunsGetter interface { + TaskRuns(namespace string) TaskRunInterface +} + +// TaskRunInterface has methods to work with TaskRun resources. +type TaskRunInterface interface { + Create(ctx context.Context, taskRun *v1.TaskRun, opts metav1.CreateOptions) (*v1.TaskRun, error) + Update(ctx context.Context, taskRun *v1.TaskRun, opts metav1.UpdateOptions) (*v1.TaskRun, error) + UpdateStatus(ctx context.Context, taskRun *v1.TaskRun, opts metav1.UpdateOptions) (*v1.TaskRun, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.TaskRun, error) + List(ctx context.Context, opts metav1.ListOptions) (*v1.TaskRunList, error) + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.TaskRun, err error) + TaskRunExpansion +} + +// taskRuns implements TaskRunInterface +type taskRuns struct { + client rest.Interface + ns string +} + +// newTaskRuns returns a TaskRuns +func newTaskRuns(c *TektonV1Client, namespace string) *taskRuns { + return &taskRuns{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the taskRun, and returns the corresponding taskRun object, and an error if there is any. +func (c *taskRuns) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.TaskRun, err error) { + result = &v1.TaskRun{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of TaskRuns that match those selectors. +func (c *taskRuns) List(ctx context.Context, opts metav1.ListOptions) (result *v1.TaskRunList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.TaskRunList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested taskRuns. +func (c *taskRuns) Watch(ctx context.Context, opts metav1.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("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a taskRun and creates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Create(ctx context.Context, taskRun *v1.TaskRun, opts metav1.CreateOptions) (result *v1.TaskRun, err error) { + result = &v1.TaskRun{} + err = c.client.Post(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a taskRun and updates it. Returns the server's representation of the taskRun, and an error, if there is any. +func (c *taskRuns) Update(ctx context.Context, taskRun *v1.TaskRun, opts metav1.UpdateOptions) (result *v1.TaskRun, err error) { + result = &v1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *taskRuns) UpdateStatus(ctx context.Context, taskRun *v1.TaskRun, opts metav1.UpdateOptions) (result *v1.TaskRun, err error) { + result = &v1.TaskRun{} + err = c.client.Put(). + Namespace(c.ns). + Resource("taskruns"). + Name(taskRun.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(taskRun). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the taskRun and deletes it. Returns an error if one occurs. +func (c *taskRuns) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *taskRuns) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("taskruns"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched taskRun. +func (c *taskRuns) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.TaskRun, err error) { + result = &v1.TaskRun{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("taskruns"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 2abfa5f70f1..1366adfcdd5 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -59,6 +59,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1().Pipelines().Informer()}, nil case v1.SchemeGroupVersion.WithResource("tasks"): return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1().Tasks().Informer()}, nil + case v1.SchemeGroupVersion.WithResource("taskruns"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Tekton().V1().TaskRuns().Informer()}, nil // Group=tekton.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("runs"): diff --git a/pkg/client/informers/externalversions/pipeline/v1/interface.go b/pkg/client/informers/externalversions/pipeline/v1/interface.go index 8a333b97c21..aa64f81144a 100644 --- a/pkg/client/informers/externalversions/pipeline/v1/interface.go +++ b/pkg/client/informers/externalversions/pipeline/v1/interface.go @@ -28,6 +28,8 @@ type Interface interface { Pipelines() PipelineInformer // Tasks returns a TaskInformer. Tasks() TaskInformer + // TaskRuns returns a TaskRunInformer. + TaskRuns() TaskRunInformer } type version struct { @@ -50,3 +52,8 @@ func (v *version) Pipelines() PipelineInformer { func (v *version) Tasks() TaskInformer { return &taskInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } + +// TaskRuns returns a TaskRunInformer. +func (v *version) TaskRuns() TaskRunInformer { + return &taskRunInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/client/informers/externalversions/pipeline/v1/taskrun.go b/pkg/client/informers/externalversions/pipeline/v1/taskrun.go new file mode 100644 index 00000000000..d8ef20c0430 --- /dev/null +++ b/pkg/client/informers/externalversions/pipeline/v1/taskrun.go @@ -0,0 +1,90 @@ +/* +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 v1 + +import ( + "context" + time "time" + + pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/internalinterfaces" + v1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1" + metav1 "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" +) + +// TaskRunInformer provides access to a shared informer and lister for +// TaskRuns. +type TaskRunInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.TaskRunLister +} + +type taskRunInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewTaskRunInformer constructs a new informer for TaskRun 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 NewTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredTaskRunInformer constructs a new informer for TaskRun 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 NewFilteredTaskRunInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1().TaskRuns(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TektonV1().TaskRuns(namespace).Watch(context.TODO(), options) + }, + }, + &pipelinev1.TaskRun{}, + resyncPeriod, + indexers, + ) +} + +func (f *taskRunInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredTaskRunInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *taskRunInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pipelinev1.TaskRun{}, f.defaultInformer) +} + +func (f *taskRunInformer) Lister() v1.TaskRunLister { + return v1.NewTaskRunLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/injection/client/client.go b/pkg/client/injection/client/client.go index 364bbf67663..edf87a0b826 100644 --- a/pkg/client/injection/client/client.go +++ b/pkg/client/injection/client/client.go @@ -1188,3 +1188,134 @@ func (w *wrapTektonV1TaskImpl) UpdateStatus(ctx context.Context, in *pipelinev1. func (w *wrapTektonV1TaskImpl) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return nil, errors.New("NYI: Watch") } + +func (w *wrapTektonV1) TaskRuns(namespace string) typedtektonv1.TaskRunInterface { + return &wrapTektonV1TaskRunImpl{ + dyn: w.dyn.Resource(schema.GroupVersionResource{ + Group: "tekton.dev", + Version: "v1", + Resource: "taskruns", + }), + + namespace: namespace, + } +} + +type wrapTektonV1TaskRunImpl struct { + dyn dynamic.NamespaceableResourceInterface + + namespace string +} + +var _ typedtektonv1.TaskRunInterface = (*wrapTektonV1TaskRunImpl)(nil) + +func (w *wrapTektonV1TaskRunImpl) Create(ctx context.Context, in *pipelinev1.TaskRun, opts v1.CreateOptions) (*pipelinev1.TaskRun, error) { + in.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "tekton.dev", + Version: "v1", + Kind: "TaskRun", + }) + uo := &unstructured.Unstructured{} + if err := convert(in, uo); err != nil { + return nil, err + } + uo, err := w.dyn.Namespace(w.namespace).Create(ctx, uo, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRun{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return w.dyn.Namespace(w.namespace).Delete(ctx, name, opts) +} + +func (w *wrapTektonV1TaskRunImpl) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + return w.dyn.Namespace(w.namespace).DeleteCollection(ctx, opts, listOpts) +} + +func (w *wrapTektonV1TaskRunImpl) Get(ctx context.Context, name string, opts v1.GetOptions) (*pipelinev1.TaskRun, error) { + uo, err := w.dyn.Namespace(w.namespace).Get(ctx, name, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRun{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) List(ctx context.Context, opts v1.ListOptions) (*pipelinev1.TaskRunList, error) { + uo, err := w.dyn.Namespace(w.namespace).List(ctx, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRunList{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *pipelinev1.TaskRun, err error) { + uo, err := w.dyn.Namespace(w.namespace).Patch(ctx, name, pt, data, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRun{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) Update(ctx context.Context, in *pipelinev1.TaskRun, opts v1.UpdateOptions) (*pipelinev1.TaskRun, error) { + in.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "tekton.dev", + Version: "v1", + Kind: "TaskRun", + }) + uo := &unstructured.Unstructured{} + if err := convert(in, uo); err != nil { + return nil, err + } + uo, err := w.dyn.Namespace(w.namespace).Update(ctx, uo, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRun{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) UpdateStatus(ctx context.Context, in *pipelinev1.TaskRun, opts v1.UpdateOptions) (*pipelinev1.TaskRun, error) { + in.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "tekton.dev", + Version: "v1", + Kind: "TaskRun", + }) + uo := &unstructured.Unstructured{} + if err := convert(in, uo); err != nil { + return nil, err + } + uo, err := w.dyn.Namespace(w.namespace).UpdateStatus(ctx, uo, opts) + if err != nil { + return nil, err + } + out := &pipelinev1.TaskRun{} + if err := convert(uo, out); err != nil { + return nil, err + } + return out, nil +} + +func (w *wrapTektonV1TaskRunImpl) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return nil, errors.New("NYI: Watch") +} diff --git a/pkg/client/injection/informers/pipeline/v1/taskrun/fake/fake.go b/pkg/client/injection/informers/pipeline/v1/taskrun/fake/fake.go new file mode 100644 index 00000000000..2d87d055f91 --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1/taskrun/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" + taskrun "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1/taskrun" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = taskrun.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Tekton().V1().TaskRuns() + return context.WithValue(ctx, taskrun.Key{}, inf), inf.Informer() +} diff --git a/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/fake/fake.go b/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/fake/fake.go new file mode 100644 index 00000000000..f5c05fda798 --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/fake/fake.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 fake + +import ( + context "context" + + factoryfiltered "github.com/tektoncd/pipeline/pkg/client/injection/informers/factory/filtered" + filtered "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1/taskrun/filtered" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +var Get = filtered.Get + +func init() { + injection.Fake.RegisterFilteredInformers(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(factoryfiltered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := factoryfiltered.Get(ctx, selector) + inf := f.Tekton().V1().TaskRuns() + ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} diff --git a/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/taskrun.go b/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/taskrun.go new file mode 100644 index 00000000000..1d5c57884de --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1/taskrun/filtered/taskrun.go @@ -0,0 +1,136 @@ +/* +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 filtered + +import ( + context "context" + + apispipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + v1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1" + client "github.com/tektoncd/pipeline/pkg/client/injection/client" + filtered "github.com/tektoncd/pipeline/pkg/client/injection/informers/factory/filtered" + pipelinev1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + cache "k8s.io/client-go/tools/cache" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterFilteredInformers(withInformer) + injection.Dynamic.RegisterDynamicInformer(withDynamicInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct { + Selector string +} + +func withInformer(ctx context.Context) (context.Context, []controller.Informer) { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + infs := []controller.Informer{} + for _, selector := range labelSelectors { + f := filtered.Get(ctx, selector) + inf := f.Tekton().V1().TaskRuns() + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + infs = append(infs, inf.Informer()) + } + return ctx, infs +} + +func withDynamicInformer(ctx context.Context) context.Context { + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + for _, selector := range labelSelectors { + inf := &wrapper{client: client.Get(ctx), selector: selector} + ctx = context.WithValue(ctx, Key{Selector: selector}, inf) + } + return ctx +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context, selector string) v1.TaskRunInformer { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1.TaskRunInformer with selector %s from context.", selector) + } + return untyped.(v1.TaskRunInformer) +} + +type wrapper struct { + client versioned.Interface + + namespace string + + selector string +} + +var _ v1.TaskRunInformer = (*wrapper)(nil) +var _ pipelinev1.TaskRunLister = (*wrapper)(nil) + +func (w *wrapper) Informer() cache.SharedIndexInformer { + return cache.NewSharedIndexInformer(nil, &apispipelinev1.TaskRun{}, 0, nil) +} + +func (w *wrapper) Lister() pipelinev1.TaskRunLister { + return w +} + +func (w *wrapper) TaskRuns(namespace string) pipelinev1.TaskRunNamespaceLister { + return &wrapper{client: w.client, namespace: namespace, selector: w.selector} +} + +func (w *wrapper) List(selector labels.Selector) (ret []*apispipelinev1.TaskRun, err error) { + reqs, err := labels.ParseToRequirements(w.selector) + if err != nil { + return nil, err + } + selector = selector.Add(reqs...) + lo, err := w.client.TektonV1().TaskRuns(w.namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: selector.String(), + // TODO(mattmoor): Incorporate resourceVersion bounds based on staleness criteria. + }) + if err != nil { + return nil, err + } + for idx := range lo.Items { + ret = append(ret, &lo.Items[idx]) + } + return ret, nil +} + +func (w *wrapper) Get(name string) (*apispipelinev1.TaskRun, error) { + // TODO(mattmoor): Check that the fetched object matches the selector. + return w.client.TektonV1().TaskRuns(w.namespace).Get(context.TODO(), name, metav1.GetOptions{ + // TODO(mattmoor): Incorporate resourceVersion bounds based on staleness criteria. + }) +} diff --git a/pkg/client/injection/informers/pipeline/v1/taskrun/taskrun.go b/pkg/client/injection/informers/pipeline/v1/taskrun/taskrun.go new file mode 100644 index 00000000000..0b7cb1be301 --- /dev/null +++ b/pkg/client/injection/informers/pipeline/v1/taskrun/taskrun.go @@ -0,0 +1,116 @@ +/* +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 taskrun + +import ( + context "context" + + apispipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + v1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1" + client "github.com/tektoncd/pipeline/pkg/client/injection/client" + factory "github.com/tektoncd/pipeline/pkg/client/injection/informers/factory" + pipelinev1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + cache "k8s.io/client-go/tools/cache" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) + injection.Dynamic.RegisterDynamicInformer(withDynamicInformer) +} + +// 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().V1().TaskRuns() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +func withDynamicInformer(ctx context.Context) context.Context { + inf := &wrapper{client: client.Get(ctx), resourceVersion: injection.GetResourceVersion(ctx)} + return context.WithValue(ctx, Key{}, inf) +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1.TaskRunInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1.TaskRunInformer from context.") + } + return untyped.(v1.TaskRunInformer) +} + +type wrapper struct { + client versioned.Interface + + namespace string + + resourceVersion string +} + +var _ v1.TaskRunInformer = (*wrapper)(nil) +var _ pipelinev1.TaskRunLister = (*wrapper)(nil) + +func (w *wrapper) Informer() cache.SharedIndexInformer { + return cache.NewSharedIndexInformer(nil, &apispipelinev1.TaskRun{}, 0, nil) +} + +func (w *wrapper) Lister() pipelinev1.TaskRunLister { + return w +} + +func (w *wrapper) TaskRuns(namespace string) pipelinev1.TaskRunNamespaceLister { + return &wrapper{client: w.client, namespace: namespace, resourceVersion: w.resourceVersion} +} + +// SetResourceVersion allows consumers to adjust the minimum resourceVersion +// used by the underlying client. It is not accessible via the standard +// lister interface, but can be accessed through a user-defined interface and +// an implementation check e.g. rvs, ok := foo.(ResourceVersionSetter) +func (w *wrapper) SetResourceVersion(resourceVersion string) { + w.resourceVersion = resourceVersion +} + +func (w *wrapper) List(selector labels.Selector) (ret []*apispipelinev1.TaskRun, err error) { + lo, err := w.client.TektonV1().TaskRuns(w.namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: selector.String(), + ResourceVersion: w.resourceVersion, + }) + if err != nil { + return nil, err + } + for idx := range lo.Items { + ret = append(ret, &lo.Items[idx]) + } + return ret, nil +} + +func (w *wrapper) Get(name string) (*apispipelinev1.TaskRun, error) { + return w.client.TektonV1().TaskRuns(w.namespace).Get(context.TODO(), name, metav1.GetOptions{ + ResourceVersion: w.resourceVersion, + }) +} diff --git a/pkg/client/injection/reconciler/pipeline/v1/taskrun/controller.go b/pkg/client/injection/reconciler/pipeline/v1/taskrun/controller.go new file mode 100644 index 00000000000..42014ac071e --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1/taskrun/controller.go @@ -0,0 +1,162 @@ +/* +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 taskrun + +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" + taskrun "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1/taskrun" + zap "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + 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" + logkey "knative.dev/pkg/logging/logkey" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "taskrun-controller" + defaultFinalizerName = "taskruns.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.ControllerOptions to be used by 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.Fatal("Up to one options function is supported, found: ", len(optionsFns)) + } + + taskrunInformer := taskrun.Get(ctx) + + lister := taskrunInformer.Lister() + + var promoteFilterFunc func(obj interface{}) bool + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + if promoteFilterFunc != nil { + if ok := promoteFilterFunc(elt); !ok { + continue + } + } + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + ctrType := reflect.TypeOf(r).Elem() + ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) + ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") + + logger = logger.With( + zap.String(logkey.ControllerType, ctrTypeName), + zap.String(logkey.Kind, "tekton.dev.TaskRun"), + ) + + impl := controller.NewContext(ctx, rec, controller.ControllerOptions{WorkQueueName: ctrTypeName, Logger: logger}) + 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 + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + if opts.PromoteFilterFunc != nil { + promoteFilterFunc = opts.PromoteFilterFunc + } + } + + 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/v1/taskrun/reconciler.go b/pkg/client/injection/reconciler/pipeline/v1/taskrun/reconciler.go new file mode 100644 index 00000000000..ff548281258 --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1/taskrun/reconciler.go @@ -0,0 +1,442 @@ +/* +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 taskrun + +import ( + context "context" + json "encoding/json" + fmt "fmt" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + versioned "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + pipelinev1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1" + zap "go.uber.org/zap" + corev1 "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" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + record "k8s.io/client-go/tools/record" + controller "knative.dev/pkg/controller" + kmp "knative.dev/pkg/kmp" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1.TaskRun. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1.TaskRun. 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 *v1.TaskRun) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1.TaskRun. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1.TaskRun. 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 *v1.TaskRun) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1.TaskRun if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1.TaskRun. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1.TaskRun) reconciler.Event +} + +type doReconcile func(ctx context.Context, o *v1.TaskRun) reconciler.Event + +// reconcilerImpl implements controller.Reconciler for v1.TaskRun resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware. + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client versioned.Interface + + // Listers index properties about resources. + Lister pipelinev1.TaskRunLister + + // 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 + + // skipStatusUpdates configures whether or not this reconciler automatically updates + // the status of the reconciled resource. + skipStatusUpdates bool +} + +// Check that our Reconciler implements controller.Reconciler. +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister pipelinev1.TaskRunLister, 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.Fatal("Up to one options struct is supported, found: ", len(options)) + } + + // Fail fast when users inadvertently implement the other LeaderAware interface. + // For the typed reconcilers, Promote shouldn't take any arguments. + if _, ok := r.(reconciler.LeaderAware); ok { + logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r) + } + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + 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 + } + if opts.SkipStatusUpdates { + rec.skipStatusUpdates = true + } + if opts.DemoteFunc != nil { + rec.DemoteFunc = opts.DemoteFunc + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(ctx) + + // Initialize the reconciler state. This will convert the namespace/name + // string into a distinct namespace and name, determine if this instance of + // the reconciler is the leader, and any additional interfaces implemented + // by the reconciler. Returns an error is the resource key is invalid. + s, err := newState(key, r) + if err != nil { + logger.Error("Invalid resource key: ", key) + return nil + } + + // If we are not the leader, and we don't implement either ReadOnly + // observer interfaces, then take a fast-path out. + if s.isNotLeaderNorObserver() { + return controller.NewSkipKey(key) + } + + // 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) + + // Get the resource with this namespace/name. + + getter := r.Lister.TaskRuns(s.namespace) + + original, err := getter.Get(s.name) + + if errors.IsNotFound(err) { + // The resource may no longer exist, in which case we stop processing and call + // the ObserveDeletion handler if appropriate. + logger.Debugf("Resource %q no longer exists", key) + if del, ok := r.reconciler.(reconciler.OnDeletionInterface); ok { + return del.ObserveDeletion(ctx, types.NamespacedName{ + Namespace: s.namespace, + Name: s.name, + }) + } + return nil + } else if err != nil { + return err + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + + name, do := s.reconcileMethodFor(resource) + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", name)) + switch name { + case reconciler.DoReconcileKind: + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = do(ctx, resource) + + case reconciler.DoFinalizeKind: + // For finalizing reconcilers, if this resource being marked for deletion + // and reconciled cleanly (nil or normal event), remove the finalizer. + reconcileEvent = do(ctx, resource) + + if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil { + return fmt.Errorf("failed to clear finalizers: %w", err) + } + + case reconciler.DoObserveKind: + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = do(ctx, resource) + + } + + // Synchronize the status. + switch { + case r.skipStatusUpdates: + // This reconciler implementation is configured to skip resource updates. + // This may mean this reconciler does not observe spec, but reconciles external changes. + case 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. + case !s.isLeader: + // High-availability reconcilers may have many replicas watching the resource, but only + // the elected leader is expected to write modifications. + logger.Warn("Saw status changes when we aren't the leader!") + default: + if err = r.updateStatus(ctx, original, resource); err != nil { + logger.Warnw("Failed to update resource status", zap.Error(err)) + r.Recorder.Eventf(resource, corev1.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.Event(resource, event.EventType, event.Reason, event.Error()) + + // the event was wrapped inside an error, consider the reconciliation as failed + if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent { + return reconcileEvent + } + return nil + } + + if controller.IsSkipKey(reconcileEvent) { + // This is a wrapped error, don't emit an event. + } else if ok, _ := controller.IsRequeueKey(reconcileEvent); ok { + // This is a wrapped error, don't emit an event. + } else { + logger.Errorw("Returned an error", zap.Error(reconcileEvent)) + r.Recorder.Event(resource, corev1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + } + return reconcileEvent + } + + return nil +} + +func (r *reconcilerImpl) updateStatus(ctx context.Context, existing *v1.TaskRun, desired *v1.TaskRun) 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.TektonV1().TaskRuns(desired.Namespace) + + existing, err = getter.Get(ctx, desired.Name, metav1.GetOptions{}) + if err != nil { + return err + } + } + + // If there's nothing to update, just return. + if equality.Semantic.DeepEqual(existing.Status, desired.Status) { + return nil + } + + if diff, err := kmp.SafeDiff(existing.Status, desired.Status); err == nil && diff != "" { + logging.FromContext(ctx).Debug("Updating status with: ", diff) + } + + existing.Status = desired.Status + + updater := r.Client.TektonV1().TaskRuns(existing.Namespace) + + _, err = updater.UpdateStatus(ctx, existing, metav1.UpdateOptions{}) + 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 *v1.TaskRun) (*v1.TaskRun, error) { + + getter := r.Lister.TaskRuns(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.TektonV1().TaskRuns(resource.Namespace) + + resourceName := resource.Name + updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + r.Recorder.Eventf(existing, corev1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(updated, corev1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return updated, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1.TaskRun) (*v1.TaskRun, 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 *v1.TaskRun, reconcileEvent reconciler.Event) (*v1.TaskRun, 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 == corev1.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/v1/taskrun/state.go b/pkg/client/injection/reconciler/pipeline/v1/taskrun/state.go new file mode 100644 index 00000000000..b989b339ba6 --- /dev/null +++ b/pkg/client/injection/reconciler/pipeline/v1/taskrun/state.go @@ -0,0 +1,97 @@ +/* +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 taskrun + +import ( + fmt "fmt" + + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + types "k8s.io/apimachinery/pkg/types" + cache "k8s.io/client-go/tools/cache" + reconciler "knative.dev/pkg/reconciler" +) + +// state is used to track the state of a reconciler in a single run. +type state struct { + // key is the original reconciliation key from the queue. + key string + // namespace is the namespace split from the reconciliation key. + namespace string + // name is the name split from the reconciliation key. + name string + // reconciler is the reconciler. + reconciler Interface + // roi is the read only interface cast of the reconciler. + roi ReadOnlyInterface + // isROI (Read Only Interface) the reconciler only observes reconciliation. + isROI bool + // isLeader the instance of the reconciler is the elected leader. + isLeader bool +} + +func newState(key string, r *reconcilerImpl) (*state, error) { + // Convert the namespace/name string into a distinct namespace and name. + namespace, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return nil, fmt.Errorf("invalid resource key: %s", key) + } + + roi, isROI := r.reconciler.(ReadOnlyInterface) + + isLeader := r.IsLeaderFor(types.NamespacedName{ + Namespace: namespace, + Name: name, + }) + + return &state{ + key: key, + namespace: namespace, + name: name, + reconciler: r.reconciler, + roi: roi, + isROI: isROI, + isLeader: isLeader, + }, nil +} + +// isNotLeaderNorObserver checks to see if this reconciler with the current +// state is enabled to do any work or not. +// isNotLeaderNorObserver returns true when there is no work possible for the +// reconciler. +func (s *state) isNotLeaderNorObserver() bool { + if !s.isLeader && !s.isROI { + // If we are not the leader, and we don't implement the ReadOnly + // interface, then take a fast-path out. + return true + } + return false +} + +func (s *state) reconcileMethodFor(o *v1.TaskRun) (string, doReconcile) { + if o.GetDeletionTimestamp().IsZero() { + if s.isLeader { + return reconciler.DoReconcileKind, s.reconciler.ReconcileKind + } else if s.isROI { + return reconciler.DoObserveKind, s.roi.ObserveKind + } + } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { + return reconciler.DoFinalizeKind, fin.FinalizeKind + } + return "unknown", nil +} diff --git a/pkg/client/listers/pipeline/v1/expansion_generated.go b/pkg/client/listers/pipeline/v1/expansion_generated.go index c2c17a833ce..bf40b83e796 100644 --- a/pkg/client/listers/pipeline/v1/expansion_generated.go +++ b/pkg/client/listers/pipeline/v1/expansion_generated.go @@ -33,3 +33,11 @@ type TaskListerExpansion interface{} // TaskNamespaceListerExpansion allows custom methods to be added to // TaskNamespaceLister. type TaskNamespaceListerExpansion interface{} + +// TaskRunListerExpansion allows custom methods to be added to +// TaskRunLister. +type TaskRunListerExpansion interface{} + +// TaskRunNamespaceListerExpansion allows custom methods to be added to +// TaskRunNamespaceLister. +type TaskRunNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/pipeline/v1/taskrun.go b/pkg/client/listers/pipeline/v1/taskrun.go new file mode 100644 index 00000000000..7f5e2c028be --- /dev/null +++ b/pkg/client/listers/pipeline/v1/taskrun.go @@ -0,0 +1,99 @@ +/* +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 v1 + +import ( + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// TaskRunLister helps list TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunLister interface { + // List lists all TaskRuns in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.TaskRun, err error) + // TaskRuns returns an object that can list and get TaskRuns. + TaskRuns(namespace string) TaskRunNamespaceLister + TaskRunListerExpansion +} + +// taskRunLister implements the TaskRunLister interface. +type taskRunLister struct { + indexer cache.Indexer +} + +// NewTaskRunLister returns a new TaskRunLister. +func NewTaskRunLister(indexer cache.Indexer) TaskRunLister { + return &taskRunLister{indexer: indexer} +} + +// List lists all TaskRuns in the indexer. +func (s *taskRunLister) List(selector labels.Selector) (ret []*v1.TaskRun, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.TaskRun)) + }) + return ret, err +} + +// TaskRuns returns an object that can list and get TaskRuns. +func (s *taskRunLister) TaskRuns(namespace string) TaskRunNamespaceLister { + return taskRunNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// TaskRunNamespaceLister helps list and get TaskRuns. +// All objects returned here must be treated as read-only. +type TaskRunNamespaceLister interface { + // List lists all TaskRuns in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.TaskRun, err error) + // Get retrieves the TaskRun from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1.TaskRun, error) + TaskRunNamespaceListerExpansion +} + +// taskRunNamespaceLister implements the TaskRunNamespaceLister +// interface. +type taskRunNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all TaskRuns in the indexer for a given namespace. +func (s taskRunNamespaceLister) List(selector labels.Selector) (ret []*v1.TaskRun, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.TaskRun)) + }) + return ret, err +} + +// Get retrieves the TaskRun from the indexer for a given namespace and name. +func (s taskRunNamespaceLister) Get(name string) (*v1.TaskRun, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("taskrun"), name) + } + return obj.(*v1.TaskRun), nil +}