-
Notifications
You must be signed in to change notification settings - Fork 139
Expand file tree
/
Copy pathlist.go
More file actions
134 lines (110 loc) · 3.62 KB
/
Copy pathlist.go
File metadata and controls
134 lines (110 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
Copyright 2022 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oci
import (
"bytes"
"context"
"fmt"
"regexp"
"sort"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/google/go-containerregistry/pkg/crane"
gcrv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/fluxcd/pkg/version"
)
// ListOptions contains options for listing tags from an OCI repository.
type ListOptions struct {
// SemverFilter contains semver for filtering tags.
SemverFilter string
// RegexFilter contains a regex that tags will be filtered by.
RegexFilter string
// IncludeCosignArtifacts can be used to include cosign attestation,
// signature and SBOM tags in the list, as these are excluded by default.
IncludeCosignArtifacts bool
}
// List fetches the tags and their manifests for a given OCI repository.
func (c *Client) List(ctx context.Context, url string, opts ListOptions) ([]Metadata, error) {
metas := make([]Metadata, 0)
tags, err := crane.ListTags(url, c.options...)
if err != nil {
return nil, fmt.Errorf("listing tags failed: %w", err)
}
sort.Slice(tags, func(i, j int) bool { return tags[i] > tags[j] })
var constraint *semver.Constraints
if opts.SemverFilter != "" {
constraint, err = semver.NewConstraint(opts.SemverFilter)
if err != nil {
return nil, fmt.Errorf("semver '%s' parse error: %w", opts.SemverFilter, err)
}
}
var re *regexp.Regexp
if opts.RegexFilter != "" {
re, err = regexp.Compile(opts.RegexFilter)
if err != nil {
return nil, fmt.Errorf("regex '%s' parse error: %w", opts.RegexFilter, err)
}
}
for _, tag := range tags {
// ignore cosign artifacts by default
if !opts.IncludeCosignArtifacts && IsCosignArtifact(tag) {
continue
}
if constraint != nil {
v, err := version.ParseVersion(tag)
// version isn't a valid semver so we can skip
if err != nil {
continue
}
if !constraint.Check(v) {
continue
}
}
if re != nil && !re.Match([]byte(tag)) {
continue
}
meta := Metadata{
URL: fmt.Sprintf("%s:%s", url, tag),
}
manifestJSON, err := crane.Manifest(meta.URL, c.optionsWithContext(ctx)...)
if err != nil {
return nil, fmt.Errorf("fetching manifest failed: %w", err)
}
manifest, err := gcrv1.ParseManifest(bytes.NewReader(manifestJSON))
if err != nil {
return nil, fmt.Errorf("parsing manifest failed: %w", err)
}
manifestMetadata := MetadataFromAnnotations(manifest.Annotations)
meta.Revision = manifestMetadata.Revision
meta.Source = manifestMetadata.Source
meta.Created = manifestMetadata.Created
digest, err := crane.Digest(meta.URL, c.optionsWithContext(ctx)...)
if err != nil {
return nil, fmt.Errorf("fetching digest failed: %w", err)
}
meta.Digest = digest
metas = append(metas, meta)
}
return metas, nil
}
// IsCosignArtifact will return true if the tag has one of the following suffices:
// ".att", ".sbom", or ".sig". These are the suffices used by cosign to store the
// attestations, SBOMs, and signatures respectively.
func IsCosignArtifact(tag string) bool {
for _, suffix := range []string{".att", ".sbom", ".sig"} {
if strings.HasSuffix(tag, suffix) {
return true
}
}
return false
}