Skip to content

Commit

Permalink
feat: show material info at chainloop att add (#1705)
Browse files Browse the repository at this point in the history
Signed-off-by: Horiodino <[email protected]>
  • Loading branch information
Horiodino authored Jan 15, 2025
1 parent 0974232 commit 620cb1d
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 13 deletions.
65 changes: 63 additions & 2 deletions app/cli/cmd/attestation_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"fmt"
"os"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/muesli/reflow/wrap"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"google.golang.org/grpc"
Expand All @@ -27,6 +29,8 @@ import (
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
)

const NotSet = "[NOT SET]"

func newAttestationAddCmd() *cobra.Command {
var name, value, kind string
var artifactCASConn *grpc.ClientConn
Expand Down Expand Up @@ -90,14 +94,21 @@ func newAttestationAddCmd() *cobra.Command {
return runWithBackoffRetry(
func() error {
// TODO: take the material output and show render it
_, err := a.Run(cmd.Context(), attestationID, name, value, kind, annotations)
resp, err := a.Run(cmd.Context(), attestationID, name, value, kind, annotations)
if err != nil {
return err
}

logger.Info().Msg("material added to attestation")

return nil
policies, err := a.GetPolicyEvaluations(cmd.Context(), attestationID)
if err != nil {
return err
}

return encodeOutput(resp, func(s *action.AttestationStatusMaterial) error {
return displayMaterialInfo(s, policies[resp.Name])
})
},
)
},
Expand Down Expand Up @@ -138,3 +149,53 @@ func newAttestationAddCmd() *cobra.Command {

return cmd
}

// displayMaterialInfo prints the material information in a table format.
func displayMaterialInfo(status *action.AttestationStatusMaterial, policyEvaluations []*action.PolicyEvaluation) error {
if status == nil {
return nil
}

mt := newTableWriter()

mt.AppendRow(table.Row{"Name", status.Material.Name})
mt.AppendRow(table.Row{"Type", status.Material.Type})
mt.AppendRow(table.Row{"Required", hBool(status.Required)})

if status.IsOutput {
mt.AppendRow(table.Row{"Is output", "Yes"})
}

if status.Value != "" {
v := status.Value
if status.Tag != "" {
v = fmt.Sprintf("%s:%s", v, status.Tag)
}
mt.AppendRow(table.Row{"Value", wrap.String(v, 100)})
}

if status.Hash != "" {
mt.AppendRow(table.Row{"Digest", status.Hash})
}

if len(status.Material.Annotations) > 0 {
mt.AppendRow(table.Row{"Annotations", "------"})
for _, a := range status.Material.Annotations {
value := a.Value
if value == "" {
value = NotSet
}
mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})
}
}

if len(policyEvaluations) > 0 {
mt.AppendRow(table.Row{"Policy evaluations", "------"})
}

policiesTable(policyEvaluations, mt)
mt.SetStyle(table.StyleLight)
mt.Style().Options.SeparateRows = true
mt.Render()
return nil
}
4 changes: 2 additions & 2 deletions app/cli/cmd/attestation_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func attestationStatusTableOutput(status *action.AttestationStatusResult, full b
for _, a := range status.Annotations {
value := a.Value
if value == "" {
value = "[NOT SET]"
value = NotSet
}
gt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})
}
Expand Down Expand Up @@ -211,7 +211,7 @@ func materialsTable(status *action.AttestationStatusResult, full bool) error {
for _, a := range m.Annotations {
value := a.Value
if value == "" {
value = "[NOT SET]"
value = NotSet
}

mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})
Expand Down
3 changes: 2 additions & 1 deletion app/cli/cmd/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ type tabulatedData interface {
[]*action.CASBackendItem |
[]*action.OrgInvitationItem |
*action.APITokenItem |
[]*action.APITokenItem
[]*action.APITokenItem |
*action.AttestationStatusMaterial
}

var ErrOutputFormatNotImplemented = errors.New("format not implemented")
Expand Down
27 changes: 27 additions & 0 deletions app/cli/internal/action/attestation_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,3 +180,30 @@ func (action *AttestationAdd) Run(ctx context.Context, attestationID, materialNa

return materialResult, nil
}

// GetPolicyEvaluations is a Wrapper around the getPolicyEvaluations
func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestationID string) (map[string][]*PolicyEvaluation, error) {
crafter, err := newCrafter(&newCrafterStateOpts{enableRemoteState: (attestationID != ""), localStatePath: action.localStatePath}, action.CPConnection, action.newCrafterOpts.opts...)
if err != nil {
return nil, fmt.Errorf("failed to load crafter: %w", err)
}

if initialized, err := crafter.AlreadyInitialized(ctx, attestationID); err != nil {
return nil, fmt.Errorf("checking if attestation is already initialized: %w", err)
} else if !initialized {
return nil, ErrAttestationNotInitialized
}

if err := crafter.LoadCraftingState(ctx, attestationID); err != nil {
action.Logger.Err(err).Msg("loading existing attestation")
return nil, err
}

policyEvaluations, _, err := getPolicyEvaluations(crafter)

if err != nil {
return nil, fmt.Errorf("getting policy evaluations: %w", err)
}

return policyEvaluations, nil
}
15 changes: 7 additions & 8 deletions app/cli/internal/action/attestation_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
v1 "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
"github.com/chainloop-dev/chainloop/pkg/attestation/renderer"
"github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop"
intoto "github.com/in-toto/attestation/go/v1"
)

type AttestationStatusOpts struct {
Expand Down Expand Up @@ -150,7 +149,12 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
return nil, fmt.Errorf("rendering statement: %w", err)
}

res.PolicyEvaluations, res.HasPolicyViolations, err = action.getPolicyEvaluations(ctx, c, attestationID, statement)
// Add attestation-level policy evaluations
if err := c.EvaluateAttestationPolicies(ctx, attestationID, statement); err != nil {
return nil, fmt.Errorf("evaluating attestation policies: %w", err)
}

res.PolicyEvaluations, res.HasPolicyViolations, err = getPolicyEvaluations(c)
if err != nil {
return nil, fmt.Errorf("getting policy evaluations: %w", err)
}
Expand Down Expand Up @@ -203,16 +207,11 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
}

// getPolicyEvaluations retrieves both material-level and attestation-level policy evaluations and returns if it has violations
func (action *AttestationStatus) getPolicyEvaluations(ctx context.Context, c *crafter.Crafter, attestationID string, statement *intoto.Statement) (map[string][]*PolicyEvaluation, bool, error) {
func getPolicyEvaluations(c *crafter.Crafter) (map[string][]*PolicyEvaluation, bool, error) {
// grouped by material name
evaluations := make(map[string][]*PolicyEvaluation)
var hasViolations bool

// Add attestation-level policy evaluations
if err := c.EvaluateAttestationPolicies(ctx, attestationID, statement); err != nil {
return nil, false, fmt.Errorf("evaluating attestation policies: %w", err)
}

// map evaluations
for _, v := range c.CraftingState.Attestation.GetPolicyEvaluations() {
keyName := v.MaterialName
Expand Down

0 comments on commit 620cb1d

Please sign in to comment.