From 873750cc4608936836bf96dce26ccc8375fcdb21 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 10 Nov 2023 10:38:12 +0100 Subject: [PATCH 1/4] improve storage Signed-off-by: Miguel Martinez Trivino --- app/controlplane/internal/biz/referrer.go | 48 ++++-- .../internal/biz/referrer_test.go | 147 +++++++++++++----- app/controlplane/internal/data/referrer.go | 14 +- 3 files changed, 147 insertions(+), 62 deletions(-) diff --git a/app/controlplane/internal/biz/referrer.go b/app/controlplane/internal/biz/referrer.go index da586aa0a..586febe6b 100644 --- a/app/controlplane/internal/biz/referrer.go +++ b/app/controlplane/internal/biz/referrer.go @@ -38,7 +38,12 @@ type Referrer struct { // Wether the item is downloadable from CAS or not Downloadable bool // points to other digests - References []string + References []*ReferrerReference +} + +type ReferrerReference struct { + Digest string + Kind string } // Actual referrer stored in the DB which includes a nested list of storedReferences @@ -142,6 +147,10 @@ const ( referrerGitHeadType = "GIT_HEAD_COMMIT" ) +func newRef(digest, kind string) string { + return fmt.Sprintf("%s-%s", kind, digest) +} + // ExtractReferrers extracts the referrers from the given attestation // this means // 1 - write an entry for the attestation itself @@ -166,12 +175,14 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { // 1 - Attestation referrer // Add the attestation itself as a referrer to the map without references yet attestationHash := h.String() - referrers[attestationHash] = &Referrer{ + attestationReferrer := &Referrer{ Digest: attestationHash, Kind: referrerAttestationType, Downloadable: true, } + referrers[newRef(attestationHash, referrerAttestationType)] = attestationReferrer + // 2 - Predicate that's referenced from the attestation predicate, err := chainloop.ExtractPredicate(att) if err != nil { @@ -189,23 +200,23 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { // Create its referrer entry if it doesn't exist yet // the reason it might exist is because you might be attaching the same material twice // i.e the same SBOM twice, in that case we don't want to create a new referrer - // If we are providing different types for the same digest, we should error out - if r, ok := referrers[material.Hash.String()]; ok { - if r.Kind != material.Type { - return nil, fmt.Errorf("material %s has different types: %s and %s", material.Hash.String(), r.Kind, material.Type) - } - + materialRef := newRef(material.Hash.String(), material.Type) + if _, ok := referrers[materialRef]; ok { continue } - referrers[material.Hash.String()] = &Referrer{ + referrers[materialRef] = &Referrer{ Digest: material.Hash.String(), Kind: material.Type, Downloadable: material.UploadedToCAS, } + materialReferrer := referrers[materialRef] + // Add the reference to the attestation - referrers[attestationHash].References = append(referrers[attestationHash].References, material.Hash.String()) + attestationReferrer.References = append(attestationReferrer.References, &ReferrerReference{ + Digest: materialReferrer.Digest, Kind: materialReferrer.Kind, + }) } // 3 - Subject that points to the attestation @@ -215,25 +226,30 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { } for _, subject := range statement.Subject { - subjectRef, err := intotoSubjectToReferrer(subject) + subjectReferrer, err := intotoSubjectToReferrer(subject) if err != nil { return nil, fmt.Errorf("transforming subject to referrer: %w", err) } - if subjectRef == nil { + if subjectReferrer == nil { continue } + subjectRef := newRef(subjectReferrer.Digest, subjectReferrer.Kind) + // check if we already have a referrer for this digest and set it otherwise // this is the case for example for git.Head ones - if _, ok := referrers[subjectRef.Digest]; !ok { - referrers[subjectRef.Digest] = subjectRef + if _, ok := referrers[subjectRef]; !ok { + referrers[subjectRef] = subjectReferrer // add it to the list of of attestation-referenced digests - referrers[attestationHash].References = append(referrers[attestationHash].References, subjectRef.Digest) + attestationReferrer.References = append(attestationReferrer.References, + &ReferrerReference{ + Digest: subjectReferrer.Digest, Kind: subjectReferrer.Kind, + }) } // Update referrer to point to the attestation - referrers[subjectRef.Digest].References = []string{attestationHash} + referrers[subjectRef].References = []*ReferrerReference{{Digest: attestationReferrer.Digest, Kind: attestationReferrer.Kind}} } return referrers, nil diff --git a/app/controlplane/internal/biz/referrer_test.go b/app/controlplane/internal/biz/referrer_test.go index bc76c5d43..4649b97fd 100644 --- a/app/controlplane/internal/biz/referrer_test.go +++ b/app/controlplane/internal/biz/referrer_test.go @@ -37,44 +37,95 @@ func (s *referrerTestSuite) TestExtractReferrers() { name: "basic", inputPath: "testdata/attestations/full.json", want: ReferrerMap{ - "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a": &Referrer{ + newRef("sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", "ATTESTATION"): &Referrer{ Digest: "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", Kind: "ATTESTATION", Downloadable: true, - References: []string{ - "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", - "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + References: []*ReferrerReference{ + { + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "CONTAINER_IMAGE", + }, + { + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + Kind: "SBOM_CYCLONEDX_JSON", + }, }, }, - "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ + newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, - "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61": &Referrer{ + newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "CONTAINER_IMAGE"): &Referrer{ Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", Kind: "CONTAINER_IMAGE", }, }, }, { - name: "basic", + name: "with string value material to be discarded", inputPath: "testdata/attestations/with-string.json", want: ReferrerMap{ // the git commit a subject in the attestation - "sha1:58442b61a6564df94857ff69ad7c340c55703e20": &Referrer{ + newRef("sha1:58442b61a6564df94857ff69ad7c340c55703e20", "GIT_HEAD_COMMIT"): &Referrer{ Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", Kind: "GIT_HEAD_COMMIT", - References: []string{ - "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + References: []*ReferrerReference{ + { + Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + Kind: "ATTESTATION", + }, + }, + }, + newRef("sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", "ATTESTATION"): &Referrer{ + Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + Kind: "ATTESTATION", + Downloadable: true, + References: []*ReferrerReference{ + { + Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + Kind: "GIT_HEAD_COMMIT", + }, }, }, - "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344": &Referrer{ - Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", - Kind: "ATTESTATION", - References: []string{ - "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + }, + }, + { + name: "with two materials with same digest", + inputPath: "testdata/attestations/with-duplicated-sha.json", + want: ReferrerMap{ + newRef("sha256:47e94045e8ffb5ea9a4939a03a21c5ad26f4ea7d463ac6ec46dac15349f45b3f", "ATTESTATION"): &Referrer{ + Digest: "sha256:47e94045e8ffb5ea9a4939a03a21c5ad26f4ea7d463ac6ec46dac15349f45b3f", + Kind: "ATTESTATION", + Downloadable: true, + References: []*ReferrerReference{ + { + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "CONTAINER_IMAGE", + }, + { + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "SBOM_CYCLONEDX_JSON", + }, + { + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + Kind: "SBOM_CYCLONEDX_JSON", + }, }, + }, + newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "CONTAINER_IMAGE"): &Referrer{ + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "CONTAINER_IMAGE", + }, + newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + Kind: "SBOM_CYCLONEDX_JSON", + Downloadable: true, + }, + newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "SBOM_CYCLONEDX_JSON"): &Referrer{ + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, }, @@ -83,59 +134,77 @@ func (s *referrerTestSuite) TestExtractReferrers() { name: "with git subject", inputPath: "testdata/attestations/with-git-subject.json", want: ReferrerMap{ - "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4": &Referrer{ + newRef("sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", "CONTAINER_IMAGE"): &Referrer{ Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", Kind: "CONTAINER_IMAGE", // the container image is a subject in the attestation - References: []string{ - "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + References: []*ReferrerReference{ + { + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + Kind: "ATTESTATION", + }, }, }, - "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721": &Referrer{ + newRef("sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", "GIT_HEAD_COMMIT"): &Referrer{ Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", Kind: "GIT_HEAD_COMMIT", // the git commit a subject in the attestation - References: []string{ - "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + References: []*ReferrerReference{ + { + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + Kind: "ATTESTATION", + }, }, }, - "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512": &Referrer{ + newRef("sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", "ARTIFACT"): &Referrer{ Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", Kind: "ARTIFACT", Downloadable: true, }, - "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95": &Referrer{ + newRef("sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", "SARIF"): &Referrer{ Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", Kind: "SARIF", Downloadable: true, }, - "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c": &Referrer{ + newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, - "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2": &Referrer{ + newRef("sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", "OPENVEX"): &Referrer{ Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", Kind: "OPENVEX", Downloadable: true, }, - "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2": &Referrer{ + newRef("sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", "ATTESTATION"): &Referrer{ Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", Kind: "ATTESTATION", Downloadable: true, - References: []string{ - // container image - "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", - // artifact - "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", - // sarif - "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", - // sbom - "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", - // openvex - "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", - // git head commit - "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + References: []*ReferrerReference{ + { + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + Kind: "CONTAINER_IMAGE", + }, + { + Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", + Kind: "ARTIFACT", + }, + { + Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", + Kind: "SARIF", + }, + { + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + Kind: "SBOM_CYCLONEDX_JSON", + }, + { + Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", + Kind: "OPENVEX", + }, + { + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + Kind: "GIT_HEAD_COMMIT", + }, }, }, }, diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go index 9b5ea8bf8..17d9fc41c 100644 --- a/app/controlplane/internal/data/referrer.go +++ b/app/controlplane/internal/data/referrer.go @@ -50,16 +50,16 @@ func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uu storedMap := make(storedReferrerMap) // 1 - Find or create each referrer - for digest, r := range input { + for id, r := range input { // Check if it exists already, if not create it - storedRef, err := tx.Referrer.Query().Where(referrer.Digest(digest), referrer.Kind(r.Kind)).Only(ctx) + storedRef, err := tx.Referrer.Query().Where(referrer.Digest(r.Digest), referrer.Kind(r.Kind)).Only(ctx) if err != nil { if !ent.IsNotFound(err) { return fmt.Errorf("failed to query referrer: %w", err) } storedRef, err = tx.Referrer.Create(). - SetDigest(digest).SetKind(r.Kind).SetDownloadable(r.Downloadable).AddOrganizationIDs(orgID).Save(ctx) + SetDigest(r.Digest).SetKind(r.Kind).SetDownloadable(r.Downloadable).AddOrganizationIDs(orgID).Save(ctx) if err != nil { return fmt.Errorf("failed to create referrer: %w", err) } @@ -72,17 +72,17 @@ func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uu } // Store it in the map - storedMap[digest] = storedRef + storedMap[id] = storedRef } // 2 - define the relationship between referrers - for digest, inputRef := range input { + for id, inputRef := range input { // This is the current item stored in DB - storedReferrer := storedMap[digest] + storedReferrer := storedMap[id] // Iterate on the items it refer to (references) for _, ref := range inputRef.References { // amd find it in the DB - storedReference, ok := storedMap[ref] + storedReference, ok := storedMap[id] if !ok { return fmt.Errorf("referrer %s not found", ref) } From 34aa384b4c563519d810b8552fb1a95cc54e7a5e Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 10 Nov 2023 11:51:47 +0100 Subject: [PATCH 2/4] improve storage Signed-off-by: Miguel Martinez Trivino --- app/controlplane/internal/biz/referrer.go | 60 ++++---- .../internal/biz/referrer_integration_test.go | 24 ++- .../internal/biz/referrer_test.go | 140 +++++++++--------- app/controlplane/internal/data/referrer.go | 32 ++-- 4 files changed, 133 insertions(+), 123 deletions(-) diff --git a/app/controlplane/internal/biz/referrer.go b/app/controlplane/internal/biz/referrer.go index 586febe6b..b5881db6d 100644 --- a/app/controlplane/internal/biz/referrer.go +++ b/app/controlplane/internal/biz/referrer.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "sort" "time" "github.com/chainloop-dev/chainloop/internal/attestation/renderer/chainloop" @@ -37,32 +38,21 @@ type Referrer struct { Kind string // Wether the item is downloadable from CAS or not Downloadable bool - // points to other digests - References []*ReferrerReference -} - -type ReferrerReference struct { - Digest string - Kind string + References []*Referrer } // Actual referrer stored in the DB which includes a nested list of storedReferences type StoredReferrer struct { - ID uuid.UUID - Digest string - Kind string - // Wether the item is downloadable from CAS or not - Downloadable bool - CreatedAt *time.Time + *Referrer + ID uuid.UUID + CreatedAt *time.Time // Fully expanded list of 1-level off references References []*StoredReferrer OrgIDs []uuid.UUID } -type ReferrerMap map[string]*Referrer - type ReferrerRepo interface { - Save(ctx context.Context, input ReferrerMap, orgID uuid.UUID) error + Save(ctx context.Context, input []*Referrer, orgID uuid.UUID) error // GetFromRoot returns the referrer identified by the provided content digest, including its first-level references // For example if sha:deadbeef represents an attestation, the result will contain the attestation + materials associated to it // OrgIDs represent an allowList of organizations where the referrers should be looked for @@ -151,6 +141,10 @@ func newRef(digest, kind string) string { return fmt.Sprintf("%s-%s", kind, digest) } +func (r *Referrer) MapID() string { + return newRef(r.Digest, r.Kind) +} + // ExtractReferrers extracts the referrers from the given attestation // this means // 1 - write an entry for the attestation itself @@ -158,7 +152,7 @@ func newRef(digest, kind string) string { // 3 - and the subjects (some of them) // 4 - creating link between the attestation and the materials/subjects as needed // see tests for examples -func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { +func extractReferrers(att *dsse.Envelope) ([]*Referrer, error) { // Calculate the attestation hash jsonAtt, err := json.Marshal(att) if err != nil { @@ -171,7 +165,7 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { return nil, fmt.Errorf("calculating attestation hash: %w", err) } - referrers := make(ReferrerMap) + referrersMap := make(map[string]*Referrer) // 1 - Attestation referrer // Add the attestation itself as a referrer to the map without references yet attestationHash := h.String() @@ -181,7 +175,7 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { Downloadable: true, } - referrers[newRef(attestationHash, referrerAttestationType)] = attestationReferrer + referrersMap[newRef(attestationHash, referrerAttestationType)] = attestationReferrer // 2 - Predicate that's referenced from the attestation predicate, err := chainloop.ExtractPredicate(att) @@ -201,20 +195,20 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { // the reason it might exist is because you might be attaching the same material twice // i.e the same SBOM twice, in that case we don't want to create a new referrer materialRef := newRef(material.Hash.String(), material.Type) - if _, ok := referrers[materialRef]; ok { + if _, ok := referrersMap[materialRef]; ok { continue } - referrers[materialRef] = &Referrer{ + referrersMap[materialRef] = &Referrer{ Digest: material.Hash.String(), Kind: material.Type, Downloadable: material.UploadedToCAS, } - materialReferrer := referrers[materialRef] + materialReferrer := referrersMap[materialRef] // Add the reference to the attestation - attestationReferrer.References = append(attestationReferrer.References, &ReferrerReference{ + attestationReferrer.References = append(attestationReferrer.References, &Referrer{ Digest: materialReferrer.Digest, Kind: materialReferrer.Kind, }) } @@ -239,17 +233,29 @@ func extractReferrers(att *dsse.Envelope) (ReferrerMap, error) { // check if we already have a referrer for this digest and set it otherwise // this is the case for example for git.Head ones - if _, ok := referrers[subjectRef]; !ok { - referrers[subjectRef] = subjectReferrer + if _, ok := referrersMap[subjectRef]; !ok { + referrersMap[subjectRef] = subjectReferrer // add it to the list of of attestation-referenced digests attestationReferrer.References = append(attestationReferrer.References, - &ReferrerReference{ + &Referrer{ Digest: subjectReferrer.Digest, Kind: subjectReferrer.Kind, }) } // Update referrer to point to the attestation - referrers[subjectRef].References = []*ReferrerReference{{Digest: attestationReferrer.Digest, Kind: attestationReferrer.Kind}} + referrersMap[subjectRef].References = []*Referrer{{Digest: attestationReferrer.Digest, Kind: attestationReferrer.Kind}} + } + + // Return a sorted list of referrers + mapKeys := make([]string, 0, len(referrersMap)) + for k := range referrersMap { + mapKeys = append(mapKeys, k) + } + sort.Strings(mapKeys) + + referrers := make([]*Referrer, 0, len(referrersMap)) + for _, k := range mapKeys { + referrers = append(referrers, referrersMap[k]) } return referrers, nil diff --git a/app/controlplane/internal/biz/referrer_integration_test.go b/app/controlplane/internal/biz/referrer_integration_test.go index 1115b49db..4b9e46c3e 100644 --- a/app/controlplane/internal/biz/referrer_integration_test.go +++ b/app/controlplane/internal/biz/referrer_integration_test.go @@ -36,42 +36,42 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { var envelope *dsse.Envelope require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) - wantReferrerAtt := &biz.StoredReferrer{ + wantReferrerAtt := &biz.Referrer{ Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", Kind: "ATTESTATION", Downloadable: true, } - wantReferrerCommit := &biz.StoredReferrer{ + wantReferrerCommit := &biz.Referrer{ Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", Kind: "GIT_HEAD_COMMIT", } - wantReferrerSBOM := &biz.StoredReferrer{ + wantReferrerSBOM := &biz.Referrer{ Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, } - wantReferrerArtifact := &biz.StoredReferrer{ + wantReferrerArtifact := &biz.Referrer{ Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", Kind: "ARTIFACT", Downloadable: true, } - wantReferrerOpenVEX := &biz.StoredReferrer{ + wantReferrerOpenVEX := &biz.Referrer{ Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", Kind: "OPENVEX", Downloadable: true, } - wantReferrerSarif := &biz.StoredReferrer{ + wantReferrerSarif := &biz.Referrer{ Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", Kind: "SARIF", Downloadable: true, } - wantReferrerContainerImage := &biz.StoredReferrer{ + wantReferrerContainerImage := &biz.Referrer{ Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", Kind: "CONTAINER_IMAGE", } @@ -109,12 +109,10 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { // it has all the references require.Len(t, got.References, 6) - for i, want := range []*biz.StoredReferrer{ + for i, want := range []*biz.Referrer{ wantReferrerCommit, wantReferrerSBOM, wantReferrerArtifact, wantReferrerOpenVEX, wantReferrerSarif, wantReferrerContainerImage} { gotR := got.References[i] - s.Equal(want.Digest, gotR.Digest) - s.Equal(want.Kind, gotR.Kind) - s.Equal(want.Downloadable, gotR.Downloadable) + s.Equal(want, gotR.Referrer) } s.Equal([]uuid.UUID{s.org1UUID}, got.OrgIDs) }) @@ -181,13 +179,13 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { s.Nil(got) }) - s.T().Run("it should fail if the attestation has the same material twice with different types", func(t *testing.T) { + s.T().Run("it should NOT fail storing the attestation with the same material twice with different types", func(t *testing.T) { attJSON, err = os.ReadFile("testdata/attestations/with-duplicated-sha.json") require.NoError(s.T(), err) require.NoError(s.T(), json.Unmarshal(attJSON, &envelope)) err := s.Referrer.ExtractAndPersist(ctx, envelope, s.org1.ID) - s.ErrorContains(err, "has different types") + s.NoError(err) }) s.T().Run("it should fail on retrieval if we have stored two referrers with same digest (for two different types)", func(t *testing.T) { diff --git a/app/controlplane/internal/biz/referrer_test.go b/app/controlplane/internal/biz/referrer_test.go index 4649b97fd..7b147192c 100644 --- a/app/controlplane/internal/biz/referrer_test.go +++ b/app/controlplane/internal/biz/referrer_test.go @@ -31,17 +31,17 @@ func (s *referrerTestSuite) TestExtractReferrers() { name string inputPath string expectErr bool - want ReferrerMap + want []*Referrer }{ { name: "basic", inputPath: "testdata/attestations/full.json", - want: ReferrerMap{ - newRef("sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", "ATTESTATION"): &Referrer{ + want: []*Referrer{ + { Digest: "sha256:1a077137aef7ca208b80c339769d0d7eecacc2850368e56e834cda1750ce413a", Kind: "ATTESTATION", Downloadable: true, - References: []*ReferrerReference{ + References: []*Referrer{ { Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", Kind: "CONTAINER_IMAGE", @@ -52,54 +52,54 @@ func (s *referrerTestSuite) TestExtractReferrers() { }, }, }, - newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ + { + Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", + Kind: "CONTAINER_IMAGE", + }, + { Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, - newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "CONTAINER_IMAGE"): &Referrer{ - Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", - Kind: "CONTAINER_IMAGE", - }, }, }, { name: "with string value material to be discarded", inputPath: "testdata/attestations/with-string.json", - want: ReferrerMap{ - // the git commit a subject in the attestation - newRef("sha1:58442b61a6564df94857ff69ad7c340c55703e20", "GIT_HEAD_COMMIT"): &Referrer{ - Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", - Kind: "GIT_HEAD_COMMIT", - References: []*ReferrerReference{ - { - Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", - Kind: "ATTESTATION", - }, - }, - }, - newRef("sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", "ATTESTATION"): &Referrer{ + want: []*Referrer{ + { Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", Kind: "ATTESTATION", Downloadable: true, - References: []*ReferrerReference{ + References: []*Referrer{ { Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", Kind: "GIT_HEAD_COMMIT", }, }, }, + // the git commit a subject in the attestation + { + Digest: "sha1:58442b61a6564df94857ff69ad7c340c55703e20", + Kind: "GIT_HEAD_COMMIT", + References: []*Referrer{ + { + Digest: "sha256:507dddb505ceb53fb32cde31f9935c9a3ebc7b7d82f36101de638b1ab9367344", + Kind: "ATTESTATION", + }, + }, + }, }, }, { name: "with two materials with same digest", inputPath: "testdata/attestations/with-duplicated-sha.json", - want: ReferrerMap{ - newRef("sha256:47e94045e8ffb5ea9a4939a03a21c5ad26f4ea7d463ac6ec46dac15349f45b3f", "ATTESTATION"): &Referrer{ + want: []*Referrer{ + { Digest: "sha256:47e94045e8ffb5ea9a4939a03a21c5ad26f4ea7d463ac6ec46dac15349f45b3f", Kind: "ATTESTATION", Downloadable: true, - References: []*ReferrerReference{ + References: []*Referrer{ { Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", Kind: "CONTAINER_IMAGE", @@ -114,16 +114,16 @@ func (s *referrerTestSuite) TestExtractReferrers() { }, }, }, - newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "CONTAINER_IMAGE"): &Referrer{ + { Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", Kind: "CONTAINER_IMAGE", }, - newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ + { Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, }, - newRef("sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", "SBOM_CYCLONEDX_JSON"): &Referrer{ + { Digest: "sha256:264f55a6ff9cec2f4742a9faacc033b29f65c04dd4480e71e23579d484288d61", Kind: "SBOM_CYCLONEDX_JSON", Downloadable: true, @@ -133,54 +133,17 @@ func (s *referrerTestSuite) TestExtractReferrers() { { name: "with git subject", inputPath: "testdata/attestations/with-git-subject.json", - want: ReferrerMap{ - newRef("sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", "CONTAINER_IMAGE"): &Referrer{ - Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", - Kind: "CONTAINER_IMAGE", - // the container image is a subject in the attestation - References: []*ReferrerReference{ - { - Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", - Kind: "ATTESTATION", - }, - }, - }, - newRef("sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", "GIT_HEAD_COMMIT"): &Referrer{ - Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", - Kind: "GIT_HEAD_COMMIT", - // the git commit a subject in the attestation - References: []*ReferrerReference{ - { - Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", - Kind: "ATTESTATION", - }, - }, - }, - newRef("sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", "ARTIFACT"): &Referrer{ + want: []*Referrer{ + { Digest: "sha256:385c4188b9c080499413f2e0fa0b3951ed107b5f0cb35c2f2b1f07a7be9a7512", Kind: "ARTIFACT", Downloadable: true, }, - newRef("sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", "SARIF"): &Referrer{ - Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", - Kind: "SARIF", - Downloadable: true, - }, - newRef("sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", "SBOM_CYCLONEDX_JSON"): &Referrer{ - Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", - Kind: "SBOM_CYCLONEDX_JSON", - Downloadable: true, - }, - newRef("sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", "OPENVEX"): &Referrer{ - Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", - Kind: "OPENVEX", - Downloadable: true, - }, - newRef("sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", "ATTESTATION"): &Referrer{ + { Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", Kind: "ATTESTATION", Downloadable: true, - References: []*ReferrerReference{ + References: []*Referrer{ { Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", Kind: "CONTAINER_IMAGE", @@ -207,6 +170,43 @@ func (s *referrerTestSuite) TestExtractReferrers() { }, }, }, + { + Digest: "sha256:fbd9335f55d83d8aaf9ab1a539b0f2a87b444e8c54f34c9a1ca9d7df15605db4", + Kind: "CONTAINER_IMAGE", + // the container image is a subject in the attestation + References: []*Referrer{ + { + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + Kind: "ATTESTATION", + }, + }, + }, + { + Digest: "sha1:78ac366c9e8a300d51808d581422ca61f7b5b721", + Kind: "GIT_HEAD_COMMIT", + // the git commit a subject in the attestation + References: []*Referrer{ + { + Digest: "sha256:ad704d286bcad6e155e71c33d48247931231338396acbcd9769087530085b2a2", + Kind: "ATTESTATION", + }, + }, + }, + { + Digest: "sha256:b4bd86d5855f94bcac0a92d3100ae7b85d050bd2e5fb9037a200e5f5f0b073a2", + Kind: "OPENVEX", + Downloadable: true, + }, + { + Digest: "sha256:c4a63494f9289dd9fd44f841efb4f5b52765c2de6332f2d86e5f6c0340b40a95", + Kind: "SARIF", + Downloadable: true, + }, + { + Digest: "sha256:16159bb881eb4ab7eb5d8afc5350b0feeed1e31c0a268e355e74f9ccbe885e0c", + Kind: "SBOM_CYCLONEDX_JSON", + Downloadable: true, + }, }, }, } diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go index 17d9fc41c..61c761fb8 100644 --- a/app/controlplane/internal/data/referrer.go +++ b/app/controlplane/internal/data/referrer.go @@ -41,7 +41,11 @@ func NewReferrerRepo(data *Data, logger log.Logger) biz.ReferrerRepo { type storedReferrerMap map[string]*ent.Referrer -func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uuid.UUID) error { +func newRef(digest, kind string) string { + return fmt.Sprintf("%s-%s", kind, digest) +} + +func (r *ReferrerRepo) Save(ctx context.Context, referrers []*biz.Referrer, orgID uuid.UUID) error { // Start transaction tx, err := r.data.db.Tx(ctx) if err != nil { @@ -50,7 +54,7 @@ func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uu storedMap := make(storedReferrerMap) // 1 - Find or create each referrer - for id, r := range input { + for _, r := range referrers { // Check if it exists already, if not create it storedRef, err := tx.Referrer.Query().Where(referrer.Digest(r.Digest), referrer.Kind(r.Kind)).Only(ctx) if err != nil { @@ -72,19 +76,19 @@ func (r *ReferrerRepo) Save(ctx context.Context, input biz.ReferrerMap, orgID uu } // Store it in the map - storedMap[id] = storedRef + storedMap[r.MapID()] = storedRef } // 2 - define the relationship between referrers - for id, inputRef := range input { + for _, r := range referrers { // This is the current item stored in DB - storedReferrer := storedMap[id] + storedReferrer := storedMap[r.MapID()] // Iterate on the items it refer to (references) - for _, ref := range inputRef.References { + for _, ref := range r.References { // amd find it in the DB - storedReference, ok := storedMap[id] + storedReference, ok := storedMap[ref.MapID()] if !ok { - return fmt.Errorf("referrer %s not found", ref) + return fmt.Errorf("referrer %v not found", ref) } // Create the relationship @@ -135,11 +139,13 @@ func (r *ReferrerRepo) doGet(ctx context.Context, digest string, orgIDs []uuid.U // Assemble the referrer to return res := &biz.StoredReferrer{ - ID: ref.ID, - CreatedAt: toTimePtr(ref.CreatedAt), - Digest: ref.Digest, - Kind: ref.Kind, - Downloadable: ref.Downloadable, + ID: ref.ID, + CreatedAt: toTimePtr(ref.CreatedAt), + Referrer: &biz.Referrer{ + Digest: ref.Digest, + Kind: ref.Kind, + Downloadable: ref.Downloadable, + }, } // with all the organizationIDs attached From 8022d6fbd15e2d1e440c51d004f156a15862d8c9 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 10 Nov 2023 12:30:25 +0100 Subject: [PATCH 3/4] improve api Signed-off-by: Miguel Martinez Trivino --- app/controlplane/internal/biz/errors.go | 16 +++++++++++++ app/controlplane/internal/biz/referrer.go | 5 ++++ app/controlplane/internal/data/referrer.go | 27 ++++++++++++++-------- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/controlplane/internal/biz/errors.go b/app/controlplane/internal/biz/errors.go index 3094ecf71..831dbf30f 100644 --- a/app/controlplane/internal/biz/errors.go +++ b/app/controlplane/internal/biz/errors.go @@ -91,3 +91,19 @@ func (e ErrUnauthorized) Error() string { func IsErrUnauthorized(err error) bool { return errors.As(err, &ErrUnauthorized{}) } + +// A referrer with the same digest points to two different artifact types +// and we require filtering out which one +type ErrAmbiguousReferrer struct { + digest string + // what kinds contain duplicates + kinds []string +} + +func NewErrReferrerAmbiguous(digest string, kinds []string) error { + return ErrAmbiguousReferrer{digest, kinds} +} + +func (e ErrAmbiguousReferrer) Error() string { + return fmt.Sprintf("digest %s present in two kinds %q", e.digest, e.kinds) +} diff --git a/app/controlplane/internal/biz/referrer.go b/app/controlplane/internal/biz/referrer.go index b5881db6d..9c5d4054f 100644 --- a/app/controlplane/internal/biz/referrer.go +++ b/app/controlplane/internal/biz/referrer.go @@ -19,6 +19,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "sort" @@ -124,6 +125,10 @@ func (s *ReferrerUseCase) GetFromRoot(ctx context.Context, digest string, userID ref, err := s.repo.GetFromRoot(ctx, digest, orgIDs) if err != nil { + if errors.As(err, &ErrAmbiguousReferrer{}) { + return nil, NewErrValidation(fmt.Errorf("please provide the referrer kind: %w", err)) + } + return nil, fmt.Errorf("getting referrer from root: %w", err) } else if ref == nil { return nil, NewErrNotFound("referrer") diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go index 61c761fb8..1c5449643 100644 --- a/app/controlplane/internal/data/referrer.go +++ b/app/controlplane/internal/data/referrer.go @@ -124,19 +124,28 @@ const maxTraverseLevels = 1 func (r *ReferrerRepo) doGet(ctx context.Context, digest string, orgIDs []uuid.UUID, level int) (*biz.StoredReferrer, error) { // Find the referrer // if there is more than 1 item with the same digest+artifactType it will fail - ref, err := r.data.db.Referrer.Query().Where(referrer.Digest(digest)). + refs, err := r.data.db.Referrer.Query().Where(referrer.Digest(digest)). Where(referrer.HasOrganizationsWith(organization.IDIn(orgIDs...))). - Only(ctx) - if err != nil { - if ent.IsNotFound(err) { - return nil, nil - } else if ent.IsNotSingular(err) { - return nil, fmt.Errorf("found more than one referrer with digest %s, please provide artifact type", digest) - } + All(ctx) + if err != nil { return nil, fmt.Errorf("failed to query referrer: %w", err) } + // No items found + if numrefs := len(refs); numrefs == 0 { + return nil, nil + } else if numrefs > 1 { + // if there is more than 1 item with the same digest+artifactType we will fail + var kinds []string + for _, r := range refs { + kinds = append(kinds, r.Kind) + } + return nil, biz.NewErrReferrerAmbiguous(digest, kinds) + } + + ref := refs[0] + // Assemble the referrer to return res := &biz.StoredReferrer{ ID: ref.ID, @@ -160,7 +169,7 @@ func (r *ReferrerRepo) doGet(ctx context.Context, digest string, orgIDs []uuid.U } // Find the references and call recursively - refs, err := ref.QueryReferences(). + refs, err = ref.QueryReferences(). Where(referrer.HasOrganizationsWith(organization.IDIn(orgIDs...))). Order(referrer.ByDigest()).All(ctx) if err != nil { From b93c5b87161fdfa4bfb80e643493692a82bf9c21 Mon Sep 17 00:00:00 2001 From: Miguel Martinez Trivino Date: Fri, 10 Nov 2023 13:25:53 +0100 Subject: [PATCH 4/4] improve api Signed-off-by: Miguel Martinez Trivino --- app/controlplane/internal/biz/errors.go | 2 +- app/controlplane/internal/biz/referrer_integration_test.go | 2 +- app/controlplane/internal/data/referrer.go | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/controlplane/internal/biz/errors.go b/app/controlplane/internal/biz/errors.go index 831dbf30f..b4e850104 100644 --- a/app/controlplane/internal/biz/errors.go +++ b/app/controlplane/internal/biz/errors.go @@ -105,5 +105,5 @@ func NewErrReferrerAmbiguous(digest string, kinds []string) error { } func (e ErrAmbiguousReferrer) Error() string { - return fmt.Sprintf("digest %s present in two kinds %q", e.digest, e.kinds) + return fmt.Sprintf("digest %s present in %d kinds %q", e.digest, len(e.kinds), e.kinds) } diff --git a/app/controlplane/internal/biz/referrer_integration_test.go b/app/controlplane/internal/biz/referrer_integration_test.go index 4b9e46c3e..f2d1f9dc2 100644 --- a/app/controlplane/internal/biz/referrer_integration_test.go +++ b/app/controlplane/internal/biz/referrer_integration_test.go @@ -201,7 +201,7 @@ func (s *referrerIntegrationTestSuite) TestExtractAndPersists() { // but retrieval should fail. In the future we will ask the user to provide the artifact type in these cases of ambiguity got, err := s.Referrer.GetFromRoot(ctx, wantReferrerSarif.Digest, s.user.ID) s.Nil(got) - s.ErrorContains(err, "found more than one referrer with digest") + s.ErrorContains(err, "present in 2 kinds") }) s.T().Run("now there should a container image pointing to two attestations", func(t *testing.T) { diff --git a/app/controlplane/internal/data/referrer.go b/app/controlplane/internal/data/referrer.go index 1c5449643..102ae1e8c 100644 --- a/app/controlplane/internal/data/referrer.go +++ b/app/controlplane/internal/data/referrer.go @@ -41,10 +41,6 @@ func NewReferrerRepo(data *Data, logger log.Logger) biz.ReferrerRepo { type storedReferrerMap map[string]*ent.Referrer -func newRef(digest, kind string) string { - return fmt.Sprintf("%s-%s", kind, digest) -} - func (r *ReferrerRepo) Save(ctx context.Context, referrers []*biz.Referrer, orgID uuid.UUID) error { // Start transaction tx, err := r.data.db.Tx(ctx)