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

RunAttestors refactor #131

Merged
merged 11 commits into from
Feb 2, 2024
7 changes: 4 additions & 3 deletions attestation/commandrun/commandrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ type CommandRun struct {

func (rc *CommandRun) Attest(ctx *attestation.AttestationContext) error {
if len(rc.Cmd) == 0 {
return attestation.ErrInvalidOption{
Option: "Cmd",
Reason: "CommandRun attestation requires a command to run",
return attestation.ErrAttestor{
Name: rc.Name(),
RunType: rc.RunType(),
Reason: "CommandRun attestation requires a command to run",
}
}

Expand Down
111 changes: 35 additions & 76 deletions attestation/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,22 @@ const (
PostProductRunType RunType = "postproduct"
)

func runTypeOrder() []RunType {
return []RunType{PreMaterialRunType, MaterialRunType, ExecuteRunType, ProductRunType, PostProductRunType}
}

func (r RunType) String() string {
return string(r)
}

type ErrInvalidOption struct {
Option string
Reason string
type ErrAttestor struct {
Name string
RunType RunType
Reason string
}

func (e ErrInvalidOption) Error() string {
return fmt.Sprintf("invalid value for option %v: %v", e.Option, e.Reason)
func (e ErrAttestor) Error() string {
return fmt.Sprintf("error returned for attestor %s of run type %s: %s", e.Name, e.RunType, e.Reason)
}

type AttestationContextOption func(ctx *AttestationContext)
Expand Down Expand Up @@ -117,82 +122,40 @@ func NewContext(attestors []Attestor, opts ...AttestationContextOption) (*Attest
}

func (ctx *AttestationContext) RunAttestors() error {
preAttestors := []Attestor{}
materialAttestors := []Attestor{}
exeucteAttestors := []Attestor{}
productAttestors := []Attestor{}
postAttestors := []Attestor{}

attestors := make(map[RunType][]Attestor)
for _, attestor := range ctx.attestors {
switch attestor.RunType() {
case PreMaterialRunType:
preAttestors = append(preAttestors, attestor)

case MaterialRunType:
materialAttestors = append(materialAttestors, attestor)

case ExecuteRunType:
exeucteAttestors = append(exeucteAttestors, attestor)

case ProductRunType:
productAttestors = append(productAttestors, attestor)

case PostProductRunType:
postAttestors = append(postAttestors, attestor)

default:
return ErrInvalidOption{
Option: "attestor.RunType",
Reason: fmt.Sprintf("unknown run type %v", attestor.RunType()),
if attestor.RunType() == "" {
return ErrAttestor{
Name: attestor.Name(),
RunType: attestor.RunType(),
Reason: "attestor run type not set",
}
}
}

for _, attestor := range preAttestors {
if err := ctx.runAttestor(attestor); err != nil {
return err
}
attestors[attestor.RunType()] = append(attestors[attestor.RunType()], attestor)
}

for _, attestor := range materialAttestors {
if err := ctx.runAttestor(attestor); err != nil {
return err
}
}

for _, attestor := range exeucteAttestors {
if err := ctx.runAttestor(attestor); err != nil {
return err
}
}

for _, attestor := range productAttestors {
if err := ctx.runAttestor(attestor); err != nil {
return err
}
}

for _, attestor := range postAttestors {
if err := ctx.runAttestor(attestor); err != nil {
return err
order := runTypeOrder()
for _, k := range order {
log.Debugf("starting %s attestors...", k.String())
for _, att := range attestors[k] {
log.Infof("Starting %v attestor...", att.Name())
ctx.runAttestor(att)
}
}

return nil
}

func (ctx *AttestationContext) runAttestor(attestor Attestor) error {
log.Infof("Starting %v attestor...", attestor.Name())
func (ctx *AttestationContext) runAttestor(attestor Attestor) {
startTime := time.Now()
if err := attestor.Attest(ctx); err != nil {
log.Errorf("Error running %v attestor: %w", attestor.Name(), err)
ctx.completedAttestors = append(ctx.completedAttestors, CompletedAttestor{
Attestor: attestor,
StartTime: startTime,
EndTime: time.Now(),
Error: err,
})
return err
}

ctx.completedAttestors = append(ctx.completedAttestors, CompletedAttestor{
Expand All @@ -205,17 +168,15 @@ func (ctx *AttestationContext) runAttestor(attestor Attestor) error {
ctx.addMaterials(materialer)
}

if producter, ok := attestor.(Producer); ok {
ctx.addProducts(producter)
if producer, ok := attestor.(Producer); ok {
ctx.addProducts(producer)
}

return nil
}

func (ctx *AttestationContext) CompletedAttestors() []CompletedAttestor {
attestors := make([]CompletedAttestor, len(ctx.completedAttestors))
copy(attestors, ctx.completedAttestors)
return attestors
out := make([]CompletedAttestor, len(ctx.completedAttestors))
copy(out, ctx.completedAttestors)
return out
}

func (ctx *AttestationContext) WorkingDir() string {
Expand All @@ -233,21 +194,19 @@ func (ctx *AttestationContext) Context() context.Context {
}

func (ctx *AttestationContext) Materials() map[string]cryptoutil.DigestSet {
matCopy := make(map[string]cryptoutil.DigestSet)
out := make(map[string]cryptoutil.DigestSet)
for k, v := range ctx.materials {
matCopy[k] = v
out[k] = v
}

return matCopy
return out
}

func (ctx *AttestationContext) Products() map[string]Product {
prodCopy := make(map[string]Product)
out := make(map[string]Product)
for k, v := range ctx.products {
prodCopy[k] = v
out[k] = v
}

return ctx.products
return out
}

func (ctx *AttestationContext) addMaterials(materialer Materialer) {
Expand Down
15 changes: 14 additions & 1 deletion run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import (
"bytes"
"encoding/json"
"errors"
"fmt"

"github.com/in-toto/go-witness/attestation"
Expand Down Expand Up @@ -82,10 +83,22 @@
return result, fmt.Errorf("failed to create attestation context: %w", err)
}

if err := runCtx.RunAttestors(); err != nil {
if err = runCtx.RunAttestors(); err != nil {
return result, fmt.Errorf("failed to run attestors: %w", err)
}

errs := make([]error, 0)
for _, r := range runCtx.CompletedAttestors() {
ChaosInTheCRD marked this conversation as resolved.
Show resolved Hide resolved
if r.Error != nil {
errs = append(errs, r.Error)
}
}

if len(errs) > 0 {
errs := append([]error{errors.New("attestors failed with error messages")}, errs...)
return result, errors.Join(errs...)

Check failure on line 99 in run.go

View workflow job for this annotation

GitHub Actions / lint

undefined: errors.Join (typecheck)
}

result.Collection = attestation.NewCollection(ro.stepName, runCtx.CompletedAttestors())
result.SignedEnvelope, err = signCollection(result.Collection, dsse.SignWithSigners(ro.signer), dsse.SignWithTimestampers(ro.timestampers...))
if err != nil {
Expand Down
Loading