Skip to content

Commit 620cb1d

Browse files
authored
feat: show material info at chainloop att add (#1705)
Signed-off-by: Horiodino <holiodin@gmail.com>
1 parent 0974232 commit 620cb1d

File tree

5 files changed

+101
-13
lines changed

5 files changed

+101
-13
lines changed

app/cli/cmd/attestation_add.go

+63-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import (
1919
"fmt"
2020
"os"
2121

22+
"github.com/jedib0t/go-pretty/v6/table"
23+
"github.com/muesli/reflow/wrap"
2224
"github.com/spf13/cobra"
2325
"github.com/spf13/viper"
2426
"google.golang.org/grpc"
@@ -27,6 +29,8 @@ import (
2729
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
2830
)
2931

32+
const NotSet = "[NOT SET]"
33+
3034
func newAttestationAddCmd() *cobra.Command {
3135
var name, value, kind string
3236
var artifactCASConn *grpc.ClientConn
@@ -90,14 +94,21 @@ func newAttestationAddCmd() *cobra.Command {
9094
return runWithBackoffRetry(
9195
func() error {
9296
// TODO: take the material output and show render it
93-
_, err := a.Run(cmd.Context(), attestationID, name, value, kind, annotations)
97+
resp, err := a.Run(cmd.Context(), attestationID, name, value, kind, annotations)
9498
if err != nil {
9599
return err
96100
}
97101

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

100-
return nil
104+
policies, err := a.GetPolicyEvaluations(cmd.Context(), attestationID)
105+
if err != nil {
106+
return err
107+
}
108+
109+
return encodeOutput(resp, func(s *action.AttestationStatusMaterial) error {
110+
return displayMaterialInfo(s, policies[resp.Name])
111+
})
101112
},
102113
)
103114
},
@@ -138,3 +149,53 @@ func newAttestationAddCmd() *cobra.Command {
138149

139150
return cmd
140151
}
152+
153+
// displayMaterialInfo prints the material information in a table format.
154+
func displayMaterialInfo(status *action.AttestationStatusMaterial, policyEvaluations []*action.PolicyEvaluation) error {
155+
if status == nil {
156+
return nil
157+
}
158+
159+
mt := newTableWriter()
160+
161+
mt.AppendRow(table.Row{"Name", status.Material.Name})
162+
mt.AppendRow(table.Row{"Type", status.Material.Type})
163+
mt.AppendRow(table.Row{"Required", hBool(status.Required)})
164+
165+
if status.IsOutput {
166+
mt.AppendRow(table.Row{"Is output", "Yes"})
167+
}
168+
169+
if status.Value != "" {
170+
v := status.Value
171+
if status.Tag != "" {
172+
v = fmt.Sprintf("%s:%s", v, status.Tag)
173+
}
174+
mt.AppendRow(table.Row{"Value", wrap.String(v, 100)})
175+
}
176+
177+
if status.Hash != "" {
178+
mt.AppendRow(table.Row{"Digest", status.Hash})
179+
}
180+
181+
if len(status.Material.Annotations) > 0 {
182+
mt.AppendRow(table.Row{"Annotations", "------"})
183+
for _, a := range status.Material.Annotations {
184+
value := a.Value
185+
if value == "" {
186+
value = NotSet
187+
}
188+
mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})
189+
}
190+
}
191+
192+
if len(policyEvaluations) > 0 {
193+
mt.AppendRow(table.Row{"Policy evaluations", "------"})
194+
}
195+
196+
policiesTable(policyEvaluations, mt)
197+
mt.SetStyle(table.StyleLight)
198+
mt.Style().Options.SeparateRows = true
199+
mt.Render()
200+
return nil
201+
}

app/cli/cmd/attestation_status.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func attestationStatusTableOutput(status *action.AttestationStatusResult, full b
109109
for _, a := range status.Annotations {
110110
value := a.Value
111111
if value == "" {
112-
value = "[NOT SET]"
112+
value = NotSet
113113
}
114114
gt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})
115115
}
@@ -211,7 +211,7 @@ func materialsTable(status *action.AttestationStatusResult, full bool) error {
211211
for _, a := range m.Annotations {
212212
value := a.Value
213213
if value == "" {
214-
value = "[NOT SET]"
214+
value = NotSet
215215
}
216216

217217
mt.AppendRow(table.Row{"", fmt.Sprintf("%s: %s", a.Name, value)})

app/cli/cmd/output.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ type tabulatedData interface {
5050
[]*action.CASBackendItem |
5151
[]*action.OrgInvitationItem |
5252
*action.APITokenItem |
53-
[]*action.APITokenItem
53+
[]*action.APITokenItem |
54+
*action.AttestationStatusMaterial
5455
}
5556

5657
var ErrOutputFormatNotImplemented = errors.New("format not implemented")

app/cli/internal/action/attestation_add.go

+27
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,30 @@ func (action *AttestationAdd) Run(ctx context.Context, attestationID, materialNa
180180

181181
return materialResult, nil
182182
}
183+
184+
// GetPolicyEvaluations is a Wrapper around the getPolicyEvaluations
185+
func (action *AttestationAdd) GetPolicyEvaluations(ctx context.Context, attestationID string) (map[string][]*PolicyEvaluation, error) {
186+
crafter, err := newCrafter(&newCrafterStateOpts{enableRemoteState: (attestationID != ""), localStatePath: action.localStatePath}, action.CPConnection, action.newCrafterOpts.opts...)
187+
if err != nil {
188+
return nil, fmt.Errorf("failed to load crafter: %w", err)
189+
}
190+
191+
if initialized, err := crafter.AlreadyInitialized(ctx, attestationID); err != nil {
192+
return nil, fmt.Errorf("checking if attestation is already initialized: %w", err)
193+
} else if !initialized {
194+
return nil, ErrAttestationNotInitialized
195+
}
196+
197+
if err := crafter.LoadCraftingState(ctx, attestationID); err != nil {
198+
action.Logger.Err(err).Msg("loading existing attestation")
199+
return nil, err
200+
}
201+
202+
policyEvaluations, _, err := getPolicyEvaluations(crafter)
203+
204+
if err != nil {
205+
return nil, fmt.Errorf("getting policy evaluations: %w", err)
206+
}
207+
208+
return policyEvaluations, nil
209+
}

app/cli/internal/action/attestation_status.go

+7-8
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
v1 "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1"
2727
"github.com/chainloop-dev/chainloop/pkg/attestation/renderer"
2828
"github.com/chainloop-dev/chainloop/pkg/attestation/renderer/chainloop"
29-
intoto "github.com/in-toto/attestation/go/v1"
3029
)
3130

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

153-
res.PolicyEvaluations, res.HasPolicyViolations, err = action.getPolicyEvaluations(ctx, c, attestationID, statement)
152+
// Add attestation-level policy evaluations
153+
if err := c.EvaluateAttestationPolicies(ctx, attestationID, statement); err != nil {
154+
return nil, fmt.Errorf("evaluating attestation policies: %w", err)
155+
}
156+
157+
res.PolicyEvaluations, res.HasPolicyViolations, err = getPolicyEvaluations(c)
154158
if err != nil {
155159
return nil, fmt.Errorf("getting policy evaluations: %w", err)
156160
}
@@ -203,16 +207,11 @@ func (action *AttestationStatus) Run(ctx context.Context, attestationID string,
203207
}
204208

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

211-
// Add attestation-level policy evaluations
212-
if err := c.EvaluateAttestationPolicies(ctx, attestationID, statement); err != nil {
213-
return nil, false, fmt.Errorf("evaluating attestation policies: %w", err)
214-
}
215-
216215
// map evaluations
217216
for _, v := range c.CraftingState.Attestation.GetPolicyEvaluations() {
218217
keyName := v.MaterialName

0 commit comments

Comments
 (0)