Skip to content

Commit 3588f21

Browse files
authored
feat(policies): make material names templatable in groups (#1498)
Signed-off-by: Jose I. Paris <jiparis@chainloop.dev>
1 parent 51e8cf1 commit 3588f21

18 files changed

+866
-363
lines changed

app/cli/internal/action/attestation_init.go

+35-5
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,10 @@ func enrichContractMaterials(ctx context.Context, schema *v1.CraftingSchema, cli
224224
}
225225
logger.Debug().Msgf("adding materials from policy group '%s'", group.GetMetadata().GetName())
226226

227-
toAdd := getGroupMaterialsToAdd(group, contractMaterials, logger)
227+
toAdd, err := getGroupMaterialsToAdd(group, pgAtt, contractMaterials, logger)
228+
if err != nil {
229+
return err
230+
}
228231
contractMaterials = append(contractMaterials, toAdd...)
229232
}
230233

@@ -234,21 +237,48 @@ func enrichContractMaterials(ctx context.Context, schema *v1.CraftingSchema, cli
234237
}
235238

236239
// merge existing materials with group ones, taking the contract's one in case of conflict
237-
func getGroupMaterialsToAdd(group *v1.PolicyGroup, fromContract []*v1.CraftingSchema_Material, logger *zerolog.Logger) []*v1.CraftingSchema_Material {
240+
func getGroupMaterialsToAdd(group *v1.PolicyGroup, pgAtt *v1.PolicyGroupAttachment, fromContract []*v1.CraftingSchema_Material, logger *zerolog.Logger) ([]*v1.CraftingSchema_Material, error) {
238241
toAdd := make([]*v1.CraftingSchema_Material, 0)
239242
for _, groupMaterial := range group.GetSpec().GetPolicies().GetMaterials() {
243+
// apply bindings if needed
244+
csm, err := groupMaterialToCraftingSchemaMaterial(groupMaterial, group, pgAtt, logger)
245+
if err != nil {
246+
return nil, err
247+
}
248+
240249
// check if material already exists in the contract and skip it in that case
241250
ignore := false
242251
for _, mat := range fromContract {
243-
if mat.GetName() == groupMaterial.GetName() {
252+
if mat.GetName() == csm.GetName() {
244253
logger.Warn().Msgf("material '%s' from policy group '%s' is also present in the contract and will be ignored", mat.GetName(), group.GetMetadata().GetName())
245254
ignore = true
246255
}
247256
}
248257
if !ignore {
249-
toAdd = append(toAdd, groupMaterial)
258+
toAdd = append(toAdd, csm)
250259
}
251260
}
252261

253-
return toAdd
262+
return toAdd, nil
263+
}
264+
265+
// translates materials and interpolates material names
266+
func groupMaterialToCraftingSchemaMaterial(gm *v1.PolicyGroup_Material, group *v1.PolicyGroup, pgAtt *v1.PolicyGroupAttachment, logger *zerolog.Logger) (*v1.CraftingSchema_Material, error) {
267+
// Validates and computes arguments
268+
args, err := policies.ComputeArguments(group.GetSpec().GetInputs(), pgAtt.GetWith(), nil, logger)
269+
if err != nil {
270+
return nil, err
271+
}
272+
273+
// Apply arguments as interpolations for materials
274+
gm, err = policies.InterpolateGroupMaterial(gm, args)
275+
if err != nil {
276+
return nil, err
277+
}
278+
279+
return &v1.CraftingSchema_Material{
280+
Type: gm.Type,
281+
Name: gm.Name,
282+
Optional: gm.Optional,
283+
}, nil
254284
}

app/cli/internal/action/attestation_init_test.go

+68-6
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ func TestEnrichMaterials(t *testing.T) {
3131
name string
3232
materials []*v1.CraftingSchema_Material
3333
policyGroup string
34+
args map[string]string
3435
expectErr bool
3536
nMaterials int
36-
nPolicies int
3737
}{
3838
{
3939
name: "existing material",
@@ -45,7 +45,6 @@ func TestEnrichMaterials(t *testing.T) {
4545
},
4646
policyGroup: "file://testdata/policy_group.yaml",
4747
nMaterials: 2,
48-
nPolicies: 0,
4948
},
5049
{
5150
name: "new materials",
@@ -57,14 +56,12 @@ func TestEnrichMaterials(t *testing.T) {
5756
},
5857
policyGroup: "file://testdata/policy_group.yaml",
5958
nMaterials: 3,
60-
nPolicies: 1,
6159
},
6260
{
6361
name: "empty materials in schema",
6462
materials: []*v1.CraftingSchema_Material{},
6563
policyGroup: "file://testdata/policy_group.yaml",
6664
nMaterials: 2,
67-
nPolicies: 1,
6865
},
6966
{
7067
name: "wrong policy group",
@@ -93,12 +90,77 @@ func TestEnrichMaterials(t *testing.T) {
9390
}
9491
require.NoError(t, err)
9592
assert.Len(t, schema.Materials, tc.nMaterials)
96-
// find "sbom" material and check it has proper policies
93+
// find "sbom" material
9794
if tc.nMaterials > 0 {
9895
assert.True(t, slices.ContainsFunc(schema.Materials, func(m *v1.CraftingSchema_Material) bool {
99-
return m.Name == "sbom" && len(m.Policies) == tc.nPolicies
96+
return m.Name == "sbom"
10097
}))
10198
}
10299
})
103100
}
104101
}
102+
103+
func TestTemplatedGroups(t *testing.T) {
104+
cases := []struct {
105+
name string
106+
materials []*v1.CraftingSchema_Material
107+
group string
108+
args map[string]string
109+
nMaterials int
110+
materialName string
111+
materialOptional bool
112+
}{
113+
{
114+
name: "interpolates material names, with defaults",
115+
materials: []*v1.CraftingSchema_Material{},
116+
group: "file://testdata/policy_group_with_arguments.yaml",
117+
nMaterials: 1,
118+
materialName: "sbom",
119+
},
120+
{
121+
name: "interpolates material names, custom material name",
122+
materials: []*v1.CraftingSchema_Material{},
123+
group: "file://testdata/policy_group_with_arguments.yaml",
124+
args: map[string]string{"sbom_name": "foo"},
125+
nMaterials: 2,
126+
materialName: "foo",
127+
},
128+
{
129+
name: "interpolates material names, custom name, with material override",
130+
materials: []*v1.CraftingSchema_Material{{
131+
Type: v1.CraftingSchema_Material_SBOM_SPDX_JSON,
132+
Name: "foo",
133+
Optional: true,
134+
},
135+
},
136+
group: "file://testdata/policy_group_with_arguments.yaml",
137+
args: map[string]string{"sbom_name": "foo"},
138+
nMaterials: 2,
139+
materialName: "foo",
140+
materialOptional: true,
141+
},
142+
}
143+
144+
l := zerolog.Nop()
145+
for _, tc := range cases {
146+
t.Run(tc.name, func(t *testing.T) {
147+
schema := v1.CraftingSchema{
148+
Materials: tc.materials,
149+
PolicyGroups: []*v1.PolicyGroupAttachment{
150+
{
151+
Ref: tc.group,
152+
With: tc.args,
153+
},
154+
{
155+
Ref: tc.group,
156+
},
157+
},
158+
}
159+
err := enrichContractMaterials(context.TODO(), &schema, nil, &l)
160+
assert.NoError(t, err)
161+
assert.Len(t, schema.Materials, tc.nMaterials)
162+
assert.Equal(t, tc.materialName, schema.Materials[0].Name)
163+
assert.Equal(t, tc.materialOptional, schema.Materials[0].Optional)
164+
})
165+
}
166+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: workflowcontract.chainloop.dev/v1
2+
kind: PolicyGroup
3+
metadata:
4+
name: sbom-quality
5+
description: This policy group applies a number of SBOM-related policies
6+
annotations:
7+
category: SBOM
8+
spec:
9+
inputs:
10+
- name: sbom_name
11+
default: "sbom"
12+
policies:
13+
materials:
14+
- name: "{{ inputs.sbom_name }}"
15+
type: SBOM_SPDX_JSON
16+
policies:
17+
- ref: file://testdata/multi-kind.yaml

0 commit comments

Comments
 (0)