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

WIP Refactor ref resolution #4108

Closed
wants to merge 1 commit into from
Closed
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
17 changes: 15 additions & 2 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/reconciler/pipelinerun"
"github.com/tektoncd/pipeline/pkg/reconciler/resolver"
"github.com/tektoncd/pipeline/pkg/reconciler/taskrun"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/rest"
Expand Down Expand Up @@ -54,6 +55,8 @@ var (
disableHighAvailability = flag.Bool("disable-ha", false, "Whether to disable high-availability functionality for this component. This flag will be deprecated "+
"and removed when we have promoted this feature to stable, so do not pass it without filing an "+
"issue upstream!")
experimentalEnableResolutionReconcilers = flag.Bool("experimental-enable-resolution-reconcilers", false,
"Enable resolution of taskrun and pipelinerun refs by experimental resolution-specific reconcilers.")
)

func main() {
Expand Down Expand Up @@ -107,10 +110,20 @@ func main() {
}()

ctx = filteredinformerfactory.WithSelectors(ctx, v1beta1.ManagedByLabelKey)
sharedmain.MainWithConfig(ctx, ControllerLogKey, cfg,

controllers := []injection.ControllerConstructor{
taskrun.NewController(*namespace, images),
pipelinerun.NewController(*namespace, images),
)
}

if *experimentalEnableResolutionReconcilers {
controllers = append(controllers,
resolver.NewTaskRunResolverController(),
resolver.NewPipelineRunResolverController(),
)
}

sharedmain.MainWithConfig(ctx, ControllerLogKey, cfg, controllers...)
}

func handler(w http.ResponseWriter, r *http.Request) {
Expand Down
8 changes: 8 additions & 0 deletions config/config-feature-flags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,11 @@ data:
# Setting this flag to "true" scopes when expressions to guard a Task only
# instead of a Task and its dependent Tasks.
scope-when-expressions-to-task: "false"
# Setting this flag to "true" prevents the TaskRun and PipelineRun
# reconcilers from performing resolution of tasks and pipelines from
# the cluster or Tekton Bundles. This resolution process can then be
# performed by external processes / other reconcilers.
#
# Warning: setting this flag to "true" without also introducing an external
# resolution mechanism will render TaskRuns and PipelineRuns inoperable.
experimental-disable-ref-resolution: "false"
44 changes: 44 additions & 0 deletions internal/resolution/clients_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2021 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 resolution

import (
"context"
"testing"

"github.com/tektoncd/pipeline/pkg/apis/config"
ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing"
"github.com/tektoncd/pipeline/test"
)

// getClients is a test helper to construct the fakes needed for
// testing ref resolution.
func getClients(t *testing.T, conf *config.Config, d test.Data) (test.Assets, func()) {
ctx, _ := ttesting.SetupFakeContext(t)
ctx, cancel := context.WithCancel(ctx)
if conf != nil {
ctx = config.ToContext(ctx, conf)
}

clients, informers := test.SeedTestData(t, ctx, d)

return test.Assets{
Ctx: ctx,
Clients: clients,
Informers: informers,
}, cancel
}
48 changes: 48 additions & 0 deletions internal/resolution/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package resolution

import (
"errors"
)

// Error embeds both a short machine-readable string reason for resolution
// problems alongside the original error generated during the resolution flow.
type Error struct {
Reason string
Original error
}

var _ error = &Error{}

// Error returns the original error's message. This is intended to meet the error.Error interface.
func (e *Error) Error() string {
return e.Original.Error()
}

// Unwrap returns the original error without the Reason annotation. This is
// intended to support usage of errors.Is and errors.As with resolution.Errors.
func (e *Error) Unwrap() error {
return e.Original
}

// NewError returns a resolution.Error with the given reason and underlying
// original error.
func NewError(reason string, err error) *Error {
return &Error{
Reason: reason,
Original: err,
}
}

var (
// ErrorTaskRunAlreadyResolved is a sentinel value that consumers of the resolution package can use to determine if a taskrun
// was already resolved and, if so, customize their fallback behaviour.
ErrorTaskRunAlreadyResolved = NewError("TaskRunAlreadyResolved", errors.New("TaskRun is already resolved"))

// ErrorPipelineRunAlreadyResolved is a sentinel value that consumers of the resolution package can use to determine if a pipelinerun
// was already resolved and, if so, customize their fallback behaviour.
ErrorPipelineRunAlreadyResolved = NewError("PipelineRunAlreadyResolved", errors.New("PipelineRun is already resolved"))

// ErrorResourceNotResolved is a sentinel value to indicate that a TaskRun or PipelineRun
// has not been resolved yet.
ErrorResourceNotResolved = NewError("ResourceNotResolved", errors.New("Resource has not been resolved"))
)
30 changes: 30 additions & 0 deletions internal/resolution/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package resolution

import (
"errors"
"testing"
)

type TestError struct{}

var _ error = &TestError{}

func (*TestError) Error() string {
return "test error"
}

func TestResolutionErrorUnwrap(t *testing.T) {
originalError := &TestError{}
resolutionError := NewError("", originalError)
if !errors.Is(resolutionError, &TestError{}) {
t.Errorf("resolution error expected to unwrap successfully")
}
}

func TestResolutionErrorMessage(t *testing.T) {
originalError := errors.New("this is just a test message")
resolutionError := NewError("", originalError)
if resolutionError.Error() != originalError.Error() {
t.Errorf("resolution error message expected to equal that of original error")
}
}
78 changes: 78 additions & 0 deletions internal/resolution/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2021 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 resolution

import (
"github.com/tektoncd/pipeline/pkg/apis/pipeline"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// CopyTaskMetaToTaskRun implements the default Tekton Pipelines behaviour
// for copying meta information to a TaskRun from the Task it references.
//
// The labels and annotations from the task will all be copied to the taskrun
// verbatim. This matches the expectations/behaviour of the current TaskRun
// reconciler.
func CopyTaskMetaToTaskRun(taskMeta *metav1.ObjectMeta, tr *v1beta1.TaskRun) {
needsLabels := len(taskMeta.Labels) > 0 || tr.Spec.TaskRef != nil
if tr.ObjectMeta.Labels == nil && needsLabels {
tr.ObjectMeta.Labels = map[string]string{}
}
for key, value := range taskMeta.Labels {
tr.ObjectMeta.Labels[key] = value
}

if tr.ObjectMeta.Annotations == nil {
tr.ObjectMeta.Annotations = make(map[string]string, len(taskMeta.Annotations))
}
for key, value := range taskMeta.Annotations {
tr.ObjectMeta.Annotations[key] = value
}

if tr.Spec.TaskRef != nil {
if tr.Spec.TaskRef.Kind == "ClusterTask" {
tr.ObjectMeta.Labels[pipeline.ClusterTaskLabelKey] = taskMeta.Name
} else {
tr.ObjectMeta.Labels[pipeline.TaskLabelKey] = taskMeta.Name
}
}
}

// CopyPipelineMetaToPipelineRun implements the default Tekton Pipelines
// behaviour for copying meta information to a PipelineRun from the Pipeline it
// references.
//
// The labels and annotations from the pipeline will all be copied to the
// pipielinerun verbatim. This matches the expectations/behaviour of the
// current PipelineRun reconciler.
func CopyPipelineMetaToPipelineRun(pipelineMeta *metav1.ObjectMeta, pr *v1beta1.PipelineRun) {
if pr.ObjectMeta.Labels == nil {
pr.ObjectMeta.Labels = make(map[string]string, len(pipelineMeta.Labels)+1)
}
for key, value := range pipelineMeta.Labels {
pr.ObjectMeta.Labels[key] = value
}
pr.ObjectMeta.Labels[pipeline.PipelineLabelKey] = pipelineMeta.Name

if pr.ObjectMeta.Annotations == nil {
pr.ObjectMeta.Annotations = make(map[string]string, len(pipelineMeta.Annotations))
}
for key, value := range pipelineMeta.Annotations {
pr.ObjectMeta.Annotations[key] = value
}
}
Loading