Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APIs for conditionals #1031

Merged
merged 1 commit into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/200-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ rules:
resources: ["mutatingwebhookconfigurations"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["tekton.dev"]
resources: ["tasks", "clustertasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources"]
resources: ["tasks", "clustertasks", "taskruns", "pipelines", "pipelineruns", "pipelineresources", "conditions"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["tekton.dev"]
resources: ["taskruns/finalizers", "pipelineruns/finalizers"]
Expand Down
31 changes: 31 additions & 0 deletions config/300-condition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Copyright 2019 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: conditions.tekton.dev
spec:
group: tekton.dev
names:
kind: Condition
plural: conditions
categories:
- all
- tekton-pipelines
scope: Namespaced
# Opt into the status subresource so metadata.generation
# starts to increment
subresources:
status: {}
version: v1alpha1
1 change: 1 addition & 0 deletions config/clusterrole-aggregate-edit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rules:
- pipelines
- pipelineruns
- pipelineresources
- conditions
verbs:
- create
- delete
Expand Down
1 change: 1 addition & 0 deletions config/clusterrole-aggregate-view.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rules:
- pipelines
- pipelineruns
- pipelineresources
- conditions
verbs:
- get
- list
Expand Down
102 changes: 102 additions & 0 deletions pkg/apis/pipeline/v1alpha1/condition_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
*
* Copyright 2019 The Tekton Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/

package v1alpha1

import (
"github.com/knative/pkg/apis"
duckv1beta1 "github.com/knative/pkg/apis/duck/v1beta1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Check that Task may be validated and defaulted.
var _ apis.Validatable = (*Condition)(nil)

// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Condition declares a step that is used to gate the execution of a Task in a Pipeline.
// A condition execution (ConditionCheck) evaluates to either true or false
// +k8s:openapi-gen=true
type Condition struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata"`

// Spec holds the desired state of the Condition from the client
// +optional
Spec ConditionSpec `json:"spec"`
}

// ConditionCheckStatus defines the observed state of ConditionCheck
type ConditionCheckStatus struct {
duckv1beta1.Status `json:",inline"`

// PodName is the name of the pod responsible for executing this condition check.
PodName string `json:"podName"`

// StartTime is the time the check is actually started.
// +optional
StartTime *metav1.Time `json:"startTime,omitempty"`

// CompletionTime is the time the check pod completed.
// +optional
CompletionTime *metav1.Time `json:"completionTime,omitempty"`

// Check describes the state of the check container.
// +optional
Check corev1.ContainerState `json:"check,omitempty"`
}

// ConditionSpec defines the desired state of the Condition
type ConditionSpec struct {
// Check declares container whose exit code determines where a condition is true or false
Check corev1.Container `json:"check,omitempty"`

// Params is an optional set of parameters which must be supplied by the user when a Condition
// is evaluated
// +optional
Params []ParamSpec `json:"params,omitempty"`
}


// ConditionCheck represents a single evaluation of a Condition step.
type ConditionCheck TaskRun
dibyom marked this conversation as resolved.
Show resolved Hide resolved


// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ConditionList contains a list of Conditions
type ConditionList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Condition `json:"items"`
}

func NewConditionCheck(tr *TaskRun) *ConditionCheck {
if tr == nil {
return nil
}

cc := ConditionCheck(*tr)
return &cc
}

41 changes: 41 additions & 0 deletions pkg/apis/pipeline/v1alpha1/condition_validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2019 The Tekton Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
"context"
dibyom marked this conversation as resolved.
Show resolved Hide resolved
"github.com/knative/pkg/apis"
"k8s.io/apimachinery/pkg/api/equality"
)

func (c Condition) Validate(ctx context.Context) *apis.FieldError {
if err := validateObjectMetadata(c.GetObjectMeta()); err != nil {
return err.ViaField("metadata")
}
return c.Spec.Validate(ctx).ViaField("Spec")
}

func (cs *ConditionSpec) Validate(ctx context.Context) *apis.FieldError {
if equality.Semantic.DeepEqual(cs, ConditionSpec{}) {
return apis.ErrMissingField(apis.CurrentField)
}

if cs.Check.Image == "" {
return apis.ErrMissingField("Check.Image")
}
return nil
}
93 changes: 93 additions & 0 deletions pkg/apis/pipeline/v1alpha1/condition_validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
*
* Copyright 2019 The Tekton Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/

package v1alpha1

import (
"context"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/knative/pkg/apis"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
)
func TestCondition_Validate(t *testing.T) {
dibyom marked this conversation as resolved.
Show resolved Hide resolved
c := Condition{
ObjectMeta: metav1.ObjectMeta{
Name: "taskname",
},
Spec: ConditionSpec{
Check: corev1.Container{
Name: "foo",
Image: "bar",
},
Params: []ParamSpec{
{
Name: "expected",
},
},
},
}
if err := c.Validate(context.Background()); err != nil {
t.Errorf("Condition.Validate() unexpected error = %v", err)
}
}

func TestCondition_Invalidate(t *testing.T) {
tcs := []struct{
name string
cond Condition
expectedError apis.FieldError
}{{
name: "invalid meta",
cond: Condition{
ObjectMeta: metav1.ObjectMeta{
Name : "invalid.,name",
},
},
expectedError: apis.FieldError{
Message: "Invalid resource name: special character . must not be present",
Paths: []string{"metadata.name"},
},
},{
name: "no image",
cond:Condition{
ObjectMeta: metav1.ObjectMeta{Name: "cond"},
Spec: ConditionSpec{
Check: corev1.Container{},
},
},
expectedError: apis.FieldError{
Message: "missing field(s)",
Paths: []string{"Spec.Check.Image"},
},
}}

for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
err := tc.cond.Validate(context.Background())
if err == nil {
t.Fatalf("Expected an Error, got nothing for %v", tc)
}
if d := cmp.Diff(tc.expectedError, *err, cmpopts.IgnoreUnexported(apis.FieldError{})); d != "" {
t.Errorf("Condition.Validate() errors diff -want, +got: %v", d)
}
})
}
}
15 changes: 15 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipeline_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ type PipelineTask struct {
// TaskRef is a reference to a task definition.
TaskRef TaskRef `json:"taskRef"`

// Conditions is a list of conditions that need to be true for the task to run
// +optional
Conditions []PipelineTaskCondition `json:"conditions,omitempty"`

// Retries represents how many times this task should be retried in case of task failure: ConditionSucceeded set to False
// +optional
Retries int `json:"retries,omitempty"`
Expand All @@ -107,6 +111,17 @@ type PipelineTaskParam struct {
Value string `json:"value"`
}

// PipelineTaskCondition allows a PipelineTask to declare a Condition to be evaluated before
// the Task is run.
type PipelineTaskCondition struct {
// ConditionRef is the name of the Condition to use for the conditionCheck
ConditionRef string `json:"conditionRef"`

// Params declare parameters passed to this Condition
// +optional
Params []Param `json:"params,omitempty"`
}

// PipelineDeclaredResource is used by a Pipeline to declare the types of the
// PipelineResources that it will required to run and names which can be used to
// refer to these PipelineResources in PipelineTaskResourceBindings.
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/pipeline/v1alpha1/pipelinerun_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ type PipelineRunTaskRunStatus struct {
// Status is the TaskRunStatus for the corresponding TaskRun
// +optional
Status *TaskRunStatus `json:"status,omitempty"`
// ConditionChecks maps the name of a condition check to its Status
// +optional
ConditionChecks map[string]*PipelineRunConditionCheckStatus `json:"conditionChecks,omitempty"`
dibyom marked this conversation as resolved.
Show resolved Hide resolved
}

type PipelineRunConditionCheckStatus struct {
// ConditionName is the name of the Condition
ConditionName string `json:"conditionName,omitempty"`
// Status is the ConditionCheckStatus for the corresponding ConditionCheck
// +optional
Status *ConditionCheckStatus `json:"status,omitempty"`
}

var pipelineRunCondSet = apis.NewBatchConditionSet()
Expand Down
Loading