Skip to content

Commit d6f4f3b

Browse files
authored
feat(attestations): Allow custom name on external evidence (#1174)
Signed-off-by: Javier Rodriguez <[email protected]>
1 parent 50a33af commit d6f4f3b

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

app/cli/internal/action/attestation_add.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,37 @@ func (action *AttestationAdd) Run(ctx context.Context, attestationID, materialNa
133133
}
134134

135135
// Add material to the attestation crafting state based on if the material is contract free or not.
136-
// By default, try to detect the material kind automatically
136+
// The checks are performed in the following order:
137+
// 1. If materialName is empty and materialType is empty, we don't know anything about the material so, we add it with auto-detected kind and random name
138+
// 2. If materialName is not empty, check if the material is in the contract. If it is, add material from contract
139+
// 2.1. If materialType is empty, try to guess the material kind with auto-detected kind and materialName
140+
// 3. If materialType is not empty, add material contract free with materialType and materialName
141+
var kind schemaapi.CraftingSchema_Material_MaterialType
137142
switch {
138143
case materialName == "" && materialType == "":
139-
var kind schemaapi.CraftingSchema_Material_MaterialType
140-
if kind, err = crafter.AddMaterialContactFreeAutomatic(ctx, attestationID, materialValue, casBackend, annotations); err != nil {
144+
kind, err = crafter.AddMaterialContactFreeWithAutoDetectedKind(ctx, attestationID, "", materialValue, casBackend, annotations)
145+
if err != nil {
141146
return fmt.Errorf("adding material: %w", err)
142147
}
143148
action.Logger.Info().Str("kind", kind.String()).Msg("material kind detected")
144149
case materialName != "":
145-
err = crafter.AddMaterialFromContract(ctx, attestationID, materialName, materialValue, casBackend, annotations)
150+
switch {
151+
// If the material is in the contract, add it from the contract
152+
case crafter.IsMaterialInContract(materialName):
153+
err = crafter.AddMaterialFromContract(ctx, attestationID, materialName, materialValue, casBackend, annotations)
154+
// If the material is not in the contract and the materialType is not provided, add material contract free with auto-detected kind, guessing the kind
155+
case materialType == "":
156+
kind, err = crafter.AddMaterialContactFreeWithAutoDetectedKind(ctx, attestationID, materialName, materialValue, casBackend, annotations)
157+
if err != nil {
158+
return fmt.Errorf("adding material: %w", err)
159+
}
160+
action.Logger.Info().Str("kind", kind.String()).Msg("material kind detected")
161+
// If the material is not in the contract and has a materialType, add material contract free with the provided materialType
162+
default:
163+
err = crafter.AddMaterialContractFree(ctx, attestationID, materialType, materialName, materialValue, casBackend, annotations)
164+
}
146165
default:
147-
err = crafter.AddMaterialContractFree(ctx, attestationID, materialType, materialValue, casBackend, annotations)
166+
err = crafter.AddMaterialContractFree(ctx, attestationID, materialType, materialName, materialValue, casBackend, annotations)
148167
}
149168

150169
if err != nil {

internal/attestation/crafter/crafter.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,8 @@ func (c *Crafter) ResolveEnvVars(ctx context.Context, attestationID string) erro
468468

469469
// AddMaterialContractFree adds a material to the crafting state without checking the contract schema.
470470
// This is useful for adding materials that are not defined in the schema.
471-
// The name of the material is automatically calculated to conform the API contract.
472-
func (c *Crafter) AddMaterialContractFree(ctx context.Context, attestationID, kind, value string, casBackend *casclient.CASBackend, runtimeAnnotations map[string]string) error {
471+
// The name of the material is automatically calculated to conform the API contract if not provided.
472+
func (c *Crafter) AddMaterialContractFree(ctx context.Context, attestationID, kind, name, value string, casBackend *casclient.CASBackend, runtimeAnnotations map[string]string) error {
473473
if err := c.requireStateLoaded(); err != nil {
474474
return fmt.Errorf("adding materials outisde the contract: %w", err)
475475
}
@@ -482,8 +482,12 @@ func (c *Crafter) AddMaterialContractFree(ctx context.Context, attestationID, ki
482482
return fmt.Errorf("%q kind not found. Available options are %q", kind, schemaapi.ListAvailableMaterialKind())
483483
}
484484

485-
// 2 - Generate a random name for the material
486-
m.Name = fmt.Sprintf("material-%d", time.Now().UnixNano())
485+
// 2 - Set the name of the material if provided
486+
m.Name = name
487+
if m.Name == "" {
488+
// 2.1 - Generate a random name for the material since it was not provided
489+
m.Name = fmt.Sprintf("material-%d", time.Now().UnixNano())
490+
}
487491

488492
// 3 - Craft resulting material
489493
return c.addMaterial(ctx, &m, attestationID, value, casBackend, runtimeAnnotations)
@@ -517,11 +521,26 @@ func (c *Crafter) AddMaterialFromContract(ctx context.Context, attestationID, ke
517521
return c.addMaterial(ctx, m, attestationID, value, casBackend, runtimeAnnotations)
518522
}
519523

520-
// AddMaterialContactFreeAutomatic adds a material to the crafting state checking the incoming material matches any of the
524+
// IsMaterialInContract checks if the material is in the contract schema
525+
func (c *Crafter) IsMaterialInContract(key string) bool {
526+
if err := c.requireStateLoaded(); err != nil {
527+
return false
528+
}
529+
530+
for _, d := range c.CraftingState.InputSchema.Materials {
531+
if d.Name == key {
532+
return true
533+
}
534+
}
535+
536+
return false
537+
}
538+
539+
// AddMaterialContactFreeWithAutoDetectedKind adds a material to the crafting state checking the incoming material matches any of the
521540
// supported types in validation order. If the material is not found it will return an error.
522-
func (c *Crafter) AddMaterialContactFreeAutomatic(ctx context.Context, attestationID, value string, casBackend *casclient.CASBackend, runtimeAnnotations map[string]string) (schemaapi.CraftingSchema_Material_MaterialType, error) {
541+
func (c *Crafter) AddMaterialContactFreeWithAutoDetectedKind(ctx context.Context, attestationID, name, value string, casBackend *casclient.CASBackend, runtimeAnnotations map[string]string) (schemaapi.CraftingSchema_Material_MaterialType, error) {
523542
for _, kind := range schemaapi.CraftingMaterialInValidationOrder {
524-
err := c.AddMaterialContractFree(ctx, attestationID, kind.String(), value, casBackend, runtimeAnnotations)
543+
err := c.AddMaterialContractFree(ctx, attestationID, kind.String(), name, value, casBackend, runtimeAnnotations)
525544
if err == nil {
526545
// Successfully added material, return the kind
527546
return kind, nil

internal/attestation/crafter/crafter_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,7 @@ func (s *crafterSuite) TestAddMaterialsAutomatic() {
474474
testCases := []struct {
475475
name string
476476
materialPath string
477+
materialName string
477478
expectedType schemaapi.CraftingSchema_Material_MaterialType
478479
uploadArtifact bool
479480
wantErr bool
@@ -498,6 +499,12 @@ func (s *crafterSuite) TestAddMaterialsAutomatic() {
498499
materialPath: "./materials/testdata/junit.xml",
499500
expectedType: schemaapi.CraftingSchema_Material_JUNIT_XML,
500501
},
502+
{
503+
name: "junit with custom material name",
504+
materialName: "custom-junit-material",
505+
materialPath: "./materials/testdata/junit.xml",
506+
expectedType: schemaapi.CraftingSchema_Material_JUNIT_XML,
507+
},
501508
{
502509
name: "artifact",
503510
materialPath: "./materials/testdata/missing-empty.tgz",
@@ -553,13 +560,18 @@ func (s *crafterSuite) TestAddMaterialsAutomatic() {
553560
c, err := newInitializedCrafter(s.T(), contract, &v1.WorkflowMetadata{}, false, "", runner)
554561
require.NoError(s.T(), err)
555562

556-
kind, err := c.AddMaterialContactFreeAutomatic(context.Background(), "random-id", tc.materialPath, backend, nil)
563+
kind, err := c.AddMaterialContactFreeWithAutoDetectedKind(context.Background(), "random-id", tc.materialName, tc.materialPath, backend, nil)
557564
if tc.wantErr {
558565
assert.ErrorIs(s.T(), err, materials.ErrBaseUploadAndCraft)
559566
} else {
560567
require.NoError(s.T(), err)
561568
}
562569
assert.Equal(s.T(), tc.expectedType.String(), kind.String())
570+
if tc.materialName != "" {
571+
attestationMaterials := c.CraftingState.Attestation.Materials
572+
_, found := attestationMaterials[tc.materialName]
573+
assert.True(s.T(), found)
574+
}
563575
})
564576
}
565577
}

0 commit comments

Comments
 (0)