Skip to content

Commit

Permalink
Add an intoto formatter.
Browse files Browse the repository at this point in the history
This translates TaskRuns to an InToto link format. Resources are mapped
to Products/Materials, and the TaskRun information is mapped to Environment.
  • Loading branch information
dlorenc committed Jul 20, 2020
1 parent c678b44 commit 576619d
Show file tree
Hide file tree
Showing 55 changed files with 6,668 additions and 1,374 deletions.
12 changes: 3 additions & 9 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import (
"github.com/tektoncd/chains/pkg/signing"
pipelineclient "github.com/tektoncd/pipeline/pkg/client/injection/client"
taskruninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1beta1/taskrun"
"github.com/tektoncd/pipeline/pkg/reconciler"
"k8s.io/client-go/tools/cache"
kubeclient "knative.dev/pkg/client/injection/kube/client"
"knative.dev/pkg/configmap"
"knative.dev/pkg/controller"
"knative.dev/pkg/injection"
Expand All @@ -48,16 +46,12 @@ func main() {
// TODO: store and use the cmw
logger := logging.FromContext(ctx)
taskRunInformer := taskruninformer.Get(ctx)
kubeclientset := kubeclient.Get(ctx)
pipelineclientset := pipelineclient.Get(ctx)

c := &tkcontroller.Reconciler{
Base: &reconciler.Base{
KubeClientSet: kubeclientset,
PipelineClientSet: pipelineclientset,
},
Logger: logger,
TaskRunLister: taskRunInformer.Lister(),
PipelineClientSet: pipelineclientset,
Logger: logger,
TaskRunLister: taskRunInformer.Lister(),
TaskRunSigner: &signing.TaskRunSigner{
Pipelineclientset: pipelineclientset,
Logger: logger,
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ go 1.14
require (
contrib.go.opencensus.io/exporter/ocagent v0.7.0 // indirect
contrib.go.opencensus.io/exporter/prometheus v0.2.0 // indirect
github.com/tektoncd/pipeline v0.13.2
github.com/in-toto/in-toto-golang v0.0.0-20200624085956-d11e3171d19a
github.com/tektoncd/pipeline v0.14.0
github.com/tektoncd/plumbing v0.0.0-20200615101855-f56d60092af6
go.uber.org/zap v1.15.0
k8s.io/apimachinery v0.17.6
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ github.com/clarketm/json v1.13.4/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQ
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudevents/sdk-go v0.0.0-20190509003705-56931988abe3/go.mod h1:j1nZWMLGg3om8SswStBoY6/SHvcLM19MuZqwDtMtmzs=
github.com/cloudevents/sdk-go/v2 v2.0.0/go.mod h1:3CTrpB4+u7Iaj6fd7E2Xvm5IxMdRoaAhqaRVnOr2rCU=
github.com/cloudevents/sdk-go/v2 v2.1.0/go.mod h1:3CTrpB4+u7Iaj6fd7E2Xvm5IxMdRoaAhqaRVnOr2rCU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
Expand Down Expand Up @@ -495,6 +496,8 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/in-toto/in-toto-golang v0.0.0-20200624085956-d11e3171d19a h1:KWC9WsZcRmWt9Is1Rd58Z7EjTNqsE2PZEAx1vDHxKQE=
github.com/in-toto/in-toto-golang v0.0.0-20200624085956-d11e3171d19a/go.mod h1:Y7lZWDb7w1+Hal8z3IQs3SAxf/+BVuHL1fm4cSGzPTo=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v0.0.0-20161215172503-049f9b42e9a5/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y=
Expand Down Expand Up @@ -775,6 +778,8 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG
github.com/tektoncd/pipeline v0.10.1/go.mod h1:D2X0exT46zYx95BU7ByM8+erpjoN7thmUBvlKThOszU=
github.com/tektoncd/pipeline v0.13.2 h1:QbjYzVplDJZB/us5OPaAS3rMpYQLJ6DKXXYFKvYhA+U=
github.com/tektoncd/pipeline v0.13.2/go.mod h1:jISolc5UsC2R9qKKiS96TvebwAW2Ihue3SXSGS1nECM=
github.com/tektoncd/pipeline v0.14.0 h1:Z3W+4x9M/qL6ByMP/+3w5uTvYC/wT9XgzcDSk3NtE6c=
github.com/tektoncd/pipeline v0.14.0/go.mod h1:AAEx5IFwl7+2xHMaEqVZkzs0eTwZqCm97mYFseDMG4Y=
github.com/tektoncd/plumbing v0.0.0-20191216083742-847dcf196de9/go.mod h1:QZHgU07PRBTRF6N57w4+ApRu8OgfYLFNqCDlfEZaD9Y=
github.com/tektoncd/plumbing v0.0.0-20200430135134-e53521e1d887/go.mod h1:cZPJIeTIoP7UPTxQyTQLs7VE1TiXJSNj0te+If4Q+jI=
github.com/tektoncd/plumbing v0.0.0-20200615101855-f56d60092af6 h1:08xTuBm3zjFK79bj1tAeK5OEYDNoYPlx1IzovuNFtI0=
Expand Down Expand Up @@ -858,6 +863,7 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand Down
10 changes: 5 additions & 5 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ import (

"github.com/tektoncd/chains/pkg/signing"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
informers "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1"
"github.com/tektoncd/pipeline/pkg/reconciler"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/tools/cache"
)

// Reconciler implements knative.dev/pkg/controller.Reconciler
type Reconciler struct {
*reconciler.Base
Logger *zap.SugaredLogger
TaskRunLister informers.TaskRunLister
TaskRunSigner signing.Signer
Logger *zap.SugaredLogger
TaskRunLister informers.TaskRunLister
TaskRunSigner signing.Signer
PipelineClientSet versioned.Interface
}

// handleTaskRun handles a changed or created TaskRun.
Expand Down
9 changes: 3 additions & 6 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
informers "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1"
fakepipelineclient "github.com/tektoncd/pipeline/pkg/client/injection/client/fake"
faketaskruninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1beta1/taskrun/fake"
"github.com/tektoncd/pipeline/pkg/reconciler"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"knative.dev/pkg/apis"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
Expand Down Expand Up @@ -125,11 +124,9 @@ func TestReconciler_handleTaskRun(t *testing.T) {
c := fakepipelineclient.Get(ctx)

r := &Reconciler{
Base: &reconciler.Base{
PipelineClientSet: c,
},
Logger: logtesting.TestLogger(t),
TaskRunSigner: signer,
PipelineClientSet: c,
Logger: logtesting.TestLogger(t),
TaskRunSigner: signer,
}
if err := r.handleTaskRun(ctx, tt.tr); err != nil {
t.Errorf("Reconciler.handleTaskRun() error = %v", err)
Expand Down
2 changes: 2 additions & 0 deletions pkg/signing/formats/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ type Payloader interface {

const (
PayloadTypeTekton = "tekton"
PayloadTypeInToto = "in-toto"
)

// AllPayloadTypes is a list of all valid Payload types.
var AllPayloadTypes = []Payloader{
&Tekton{},
&InToto{},
}
78 changes: 78 additions & 0 deletions pkg/signing/formats/intoto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
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.
*/

package formats

import (
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
)

// InToto is a formatter that translates a TaskRun to an InToto link file
type InToto struct {
}

// CreatePayload implements the Payloader interface.
func (i *InToto) CreatePayload(tr *v1beta1.TaskRun) (interface{}, error) {
// Here we translate a Tekton TaskRun into an InToto Link file.
// At a high leevel, the mapping looks roughly like:
// Input Resource Results -> Materials
// Output Resource Results -> Products
// The entire TaskRun body -> Environment

l := in_toto.Link{
Type: "_link",
}

// Populate materials with resource inputs.
l.Materials = map[string]interface{}{}
if tr.Spec.Resources != nil {
for _, r := range tr.Spec.Resources.Inputs {
for _, rr := range tr.Status.ResourcesResult {
if r.Name == rr.ResourceName {
if _, ok := l.Materials[rr.ResourceName]; !ok {
l.Materials[rr.ResourceName] = map[string]string{}
}
m := l.Materials[rr.ResourceName].(map[string]string)
m[rr.Key] = rr.Value
}
}
}

// Populate products with resource outputs.
l.Products = map[string]interface{}{}
for _, r := range tr.Spec.Resources.Outputs {
for _, rr := range tr.Status.ResourcesResult {
if r.Name == rr.ResourceName {
if _, ok := l.Products[rr.ResourceName]; !ok {
l.Products[rr.ResourceName] = map[string]string{}
}
m := l.Products[rr.ResourceName].(map[string]string)
m[rr.Key] = rr.Value
}
}
}
}

// The environment should contain info about Tekton itself,
// and the container images used in the TaskRun.
l.Environment = map[string]interface{}{}
l.Environment["tekton"] = tr.Status

// TODO: Add Tekton release info here
return l, nil
}

func (i *InToto) Type() string {
return PayloadTypeInToto
}
157 changes: 157 additions & 0 deletions pkg/signing/formats/intoto_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
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.
*/

package formats

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/in-toto/in-toto-golang/in_toto"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
)

func TestInToto_CreatePayload(t *testing.T) {
tests := []struct {
name string
tr *v1beta1.TaskRun
want interface{}
}{
{
name: "single input",
tr: newBuilder().addInput("my-input").addResult("commit", "foo", "my-input").TaskRun,
want: in_toto.Link{
Type: "_link",
Materials: map[string]interface{}{
"my-input": map[string]string{
"commit": "foo",
},
},
Products: map[string]interface{}{},
},
},
{
name: "multi input",
tr: newBuilder().addInput("my-input").addResult("commit", "foo", "my-input").addInput("my-other-input").addResult("digest", "baz", "my-other-input").TaskRun,
want: in_toto.Link{
Type: "_link",
Materials: map[string]interface{}{
"my-input": map[string]string{
"commit": "foo",
},
"my-other-input": map[string]string{
"digest": "baz",
},
},
Products: map[string]interface{}{},
},
},
{
name: "extra results",
tr: newBuilder().addInput("my-input").addResult("commit", "foo", "my-input").addResult("digest", "baz", "my-other-input").TaskRun,
want: in_toto.Link{
Type: "_link",
Materials: map[string]interface{}{
"my-input": map[string]string{
"commit": "foo",
},
},
Products: map[string]interface{}{},
},
},
{
name: "inputs and outputs",
tr: newBuilder().addInput("my-input").addResult("commit", "foo", "my-input").addOutput("my-output").addResult("digest", "baz", "my-output").TaskRun,
want: in_toto.Link{
Type: "_link",
Materials: map[string]interface{}{
"my-input": map[string]string{
"commit": "foo",
},
},
Products: map[string]interface{}{
"my-output": map[string]string{
"digest": "baz",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := &InToto{}
got, err := i.CreatePayload(tt.tr)

// Every test case should have "tekton" field of the link file set.
link, ok := tt.want.(in_toto.Link)
if !ok {
t.Error("error casting interface to link file")
}
if link.Environment == nil {
link.Environment = map[string]interface{}{}
}

link.Environment["tekton"] = tt.tr.Status
if err != nil {
t.Errorf("InToto.CreatePayload() error = %v", err)
return
}
if diff := cmp.Diff(got, link); diff != "" {
t.Errorf("InToto.CreatePayload(): -want +got: %s", diff)
}
})
}
}

type trBuilder struct {
*v1beta1.TaskRun
}

func newBuilder() *trBuilder {
return &trBuilder{
TaskRun: &v1beta1.TaskRun{},
}
}

func (trb *trBuilder) addInput(name string) *trBuilder {
if trb.Spec.Resources == nil {
trb.Spec.Resources = &v1beta1.TaskRunResources{}
}
trb.Spec.Resources.Inputs = append(trb.Spec.Resources.Inputs, v1beta1.TaskResourceBinding{
PipelineResourceBinding: v1beta1.PipelineResourceBinding{
Name: name,
},
})
return trb
}

func (trb *trBuilder) addOutput(name string) *trBuilder {
if trb.Spec.Resources == nil {
trb.Spec.Resources = &v1beta1.TaskRunResources{}
}
trb.Spec.Resources.Outputs = append(trb.Spec.Resources.Outputs, v1beta1.TaskResourceBinding{
PipelineResourceBinding: v1beta1.PipelineResourceBinding{
Name: name,
},
})
return trb
}

func (trb *trBuilder) addResult(k, v, n string) *trBuilder {
trb.Status.ResourcesResult = append(trb.Status.ResourcesResult, v1beta1.PipelineResourceResult{
Key: k,
Value: v,
ResourceName: n,
})
return trb
}
6 changes: 4 additions & 2 deletions pkg/signing/storage/tekton/tekton.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package tekton
import (
"encoding/base64"
"encoding/json"
"fmt"

"github.com/tektoncd/chains/pkg/patch"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
Expand All @@ -26,7 +27,7 @@ import (

const (
StorageBackendTekton = "tekton"
PayloadAnnotation = "chains.tekton.dev/payload"
PayloadAnnotation = "chains.tekton.dev/%s-payload"
)

// Tekton is a storage backend that stores signed payloads in the TaskRun metadata as an annotation.
Expand Down Expand Up @@ -55,9 +56,10 @@ func (b *Backend) StorePayload(payload interface{}, payloadType string, tr *v1be

textPayload := base64.StdEncoding.EncodeToString(jsonPayload)

formatAnnotation := fmt.Sprintf(PayloadAnnotation, payloadType)
// Use patch instead of update to prevent race conditions.
patchBytes, err := patch.GetAnnotationsPatch(map[string]string{
PayloadAnnotation: textPayload,
formatAnnotation: textPayload,
})
if err != nil {
return err
Expand Down
Loading

0 comments on commit 576619d

Please sign in to comment.