Skip to content

Commit 4c59569

Browse files
authored
Merge pull request #124 from chainguard-dev/create-pull-request/patch
Export mono/sdk: refs/heads/main
2 parents 803c9ba + e74af2c commit 4c59569

File tree

13 files changed

+918
-721
lines changed

13 files changed

+918
-721
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
contents: read
2222
steps:
2323
- name: Check out code onto GOPATH
24-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
24+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
2525

2626
# https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds
2727
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
@@ -37,7 +37,7 @@ jobs:
3737
${{ runner.os }}-go-
3838
3939
- name: Set up Go
40-
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
40+
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
4141
with:
4242
go-version-file: './go.mod'
4343
check-latest: true

.github/workflows/verify.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ jobs:
2626

2727
steps:
2828
- name: Check out code
29-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
29+
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
3030
with:
3131
path: ./src/github.com/${{ github.repository }}
3232
fetch-depth: 0
3333
persist-credentials: false
3434

3535
- name: Set up Go
36-
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
36+
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
3737
with:
3838
go-version-file: './src/github.com/${{ github.repository }}/go.mod'
3939
check-latest: true
@@ -81,7 +81,7 @@ jobs:
8181
./hack/update-codegen.sh
8282
8383
- name: Verify
84-
uses: chainguard-dev/actions/nodiff@abcc11e1cf9073eff6c69e91c49756c1430b094c # v1.5.8
84+
uses: chainguard-dev/actions/nodiff@b479012116eacde7f895586c17b598f7ba0ee700 # v1.5.9
8585
with:
8686
path: ./src/github.com/${{ github.repository }}
8787
fixup-command: "./hack/update-codegen.sh"

auth/token/token.go

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"os"
1313
"path/filepath"
1414
"runtime"
15+
"slices"
16+
"sort"
1517
"strings"
1618
"time"
1719

@@ -27,15 +29,41 @@ const (
2729

2830
var (
2931
AllKinds = []Kind{KindAccess, KindRefresh}
30-
)
3132

32-
var (
3333
parentDir = "chainguard"
3434
)
3535

36+
type token struct {
37+
alias string
38+
}
39+
40+
type Option func(*token)
41+
42+
// WithAlias allows callers to organize tokens into subdirectories
43+
// under an audience to manage multiple tokens for the same audience
44+
// without overwriting.
45+
func WithAlias(a string) Option {
46+
return func(token *token) {
47+
token.alias = a
48+
}
49+
}
50+
51+
func newToken(opts ...Option) token {
52+
t := token{}
53+
for _, o := range opts {
54+
o(&t)
55+
}
56+
return t
57+
}
58+
3659
// Save saves the given token to cache/audience
37-
func Save(token []byte, kind Kind, audience string) error {
38-
path, err := Path(kind, audience)
60+
func Save(token []byte, kind Kind, audience string, opts ...Option) error {
61+
t := newToken(opts...)
62+
return t.save(token, kind, audience)
63+
}
64+
65+
func (t token) save(token []byte, kind Kind, audience string) error {
66+
path, err := t.path(kind, audience)
3967
if err != nil {
4068
return err
4169
}
@@ -48,8 +76,13 @@ func Save(token []byte, kind Kind, audience string) error {
4876

4977
// Load returns the token for the given audience if it exists,
5078
// or an error if it doesn't.
51-
func Load(kind Kind, audience string) ([]byte, error) {
52-
path, err := Path(kind, audience)
79+
func Load(kind Kind, audience string, opts ...Option) ([]byte, error) {
80+
t := newToken(opts...)
81+
return t.load(kind, audience)
82+
}
83+
84+
func (t token) load(kind Kind, audience string) ([]byte, error) {
85+
path, err := t.path(kind, audience)
5386
if err != nil {
5487
return nil, err
5588
}
@@ -63,8 +96,13 @@ func Load(kind Kind, audience string) ([]byte, error) {
6396

6497
// Delete removes the token for the given audience, if it exists.
6598
// No error is returned if the token doesn't exist.
66-
func Delete(kind Kind, audience string) error {
67-
path, err := Path(kind, audience)
99+
func Delete(kind Kind, audience string, opts ...Option) error {
100+
t := newToken(opts...)
101+
return t.delete(kind, audience)
102+
}
103+
104+
func (t token) delete(kind Kind, audience string) error {
105+
path, err := t.path(kind, audience)
68106
if err != nil {
69107
return err
70108
}
@@ -81,56 +119,96 @@ func DeleteAll() error {
81119
if err != nil {
82120
return fmt.Errorf("error locating Chainguard token dir: %w", err)
83121
}
84-
files, err := os.ReadDir(base)
85-
if err != nil {
86-
return fmt.Errorf("error reading Chainguard token dir: %w", err)
87-
}
122+
88123
// Token directory is expected to be structured as group of audience-specific
89-
// directories, with a single file containing the token
124+
// directories, with token files (oidc-token, refresh-token) or alias directories
125+
// nested within.
90126
//
91127
// $ tree ~/Library/Caches/chainguard
92128
// /Users/foo/Library/Caches/chainguard
93129
// ├── https:--console-api.enforce.dev
94-
// │ └── oidc-token
95-
// ├── https:--cgr.dev
96-
// └── oidc-token
97-
for _, file := range files {
98-
if !file.IsDir() {
99-
// Encountered a file in the directory. Skip.
100-
continue
130+
// │ ├── oidc-token
131+
// │ ├── refresh-token
132+
// │ └── foo
133+
// │ ├── oidc-token
134+
// │ └── refresh-token
135+
// ├── cgr.dev
136+
// └── oidc-token
137+
var dirs []string
138+
if err = filepath.WalkDir(base, func(path string, d fs.DirEntry, err error) error {
139+
// Return errors encountered reading the base directory.
140+
if err != nil {
141+
return err
101142
}
102-
for _, kind := range AllKinds {
103-
// Try to remove a token, ignore file not exist errors
104-
tokenFile := filepath.Join(base, file.Name(), string(kind))
105-
if err := os.Remove(tokenFile); err != nil && !errors.Is(err, fs.ErrNotExist) {
106-
return fmt.Errorf("failed to remove %s: %w", tokenFile, err)
143+
144+
switch {
145+
case path == base:
146+
// Skip the base directory, we don't want to remove it
147+
case d.IsDir():
148+
// Keep track of directories we'll want to delete, if they end up empty.
149+
dirs = append(dirs, path)
150+
case slices.Contains(AllKinds, Kind(d.Name())):
151+
// Remove recognized token files.
152+
if err := os.Remove(path); err != nil {
153+
return fmt.Errorf("removing file %s: %w", path, err)
107154
}
108155
}
109-
// Remove the (hopefully empty) audience directory.
110-
// Ignore failures since other tools may have stored files in this cache.
111-
dir := filepath.Join(base, file.Name())
112-
_ = os.Remove(dir)
156+
return nil
157+
}); err != nil {
158+
return err
113159
}
160+
161+
// Sort directories from longest to shortest to remove nested dirs first.
162+
sort.Slice(dirs, func(i, j int) bool {
163+
return len(dirs[i]) > len(dirs[j])
164+
})
165+
166+
// Remove empty directories
167+
for _, d := range dirs {
168+
ents, err := os.ReadDir(d)
169+
if err != nil {
170+
return fmt.Errorf("reading %s: %w", d, err)
171+
}
172+
// Remove the directory if it is empty
173+
if len(ents) == 0 {
174+
if err := os.Remove(d); err != nil {
175+
return fmt.Errorf("removing directory %s: %w", d, err)
176+
}
177+
}
178+
}
179+
114180
return nil
115181
}
116182

117183
// Path is the filepath of the token for the given audience.
118-
func Path(kind Kind, audience string) (string, error) {
184+
func Path(kind Kind, audience string, opts ...Option) (string, error) {
185+
t := newToken(opts...)
186+
return t.path(kind, audience)
187+
}
188+
189+
func (t token) path(kind Kind, audience string) (string, error) {
119190
a := strings.ReplaceAll(audience, "/", "-")
120191
// Windows does not allow : as a valid character for directory names.
121192
// For backwards compatibility, keep : in directory names for non-Windows systems.
122193
// Ref: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
123194
if runtime.GOOS == "windows" {
124195
a = strings.ReplaceAll(a, ":", "-")
125196
}
126-
fp := filepath.Join(a, string(kind))
197+
// NB: empty elements in Join are ignored, so we don't need to
198+
// check the existence of t.alias here.
199+
fp := filepath.Join(a, t.alias, string(kind))
127200
return cacheFilePath(fp)
128201
}
129202

130203
// RemainingLife returns the amount of time remaining before the token for
131204
// the given audience expires. Returns 0 for expired and non-existent tokens.
132-
func RemainingLife(kind Kind, audience string, less time.Duration) time.Duration {
133-
tok, err := Load(kind, audience)
205+
func RemainingLife(kind Kind, audience string, less time.Duration, opts ...Option) time.Duration {
206+
t := newToken(opts...)
207+
return t.remainingLife(kind, audience, less)
208+
}
209+
210+
func (t token) remainingLife(kind Kind, audience string, less time.Duration) time.Duration {
211+
tok, err := t.load(kind, audience)
134212
if err != nil {
135213
// Not a big deal, life is zero.
136214
return 0
@@ -156,10 +234,7 @@ var timeUntil = time.Until
156234
// Safe calculation for duration remaining from a given time, less the given duration.
157235
func subtractOrZero(expiry time.Time, less time.Duration) time.Duration {
158236
life := timeUntil(expiry.Add(less * -1))
159-
if life < 0 {
160-
return 0
161-
}
162-
return life
237+
return max(0, life)
163238
}
164239

165240
func cacheFilePath(file string) (string, error) {

0 commit comments

Comments
 (0)