Skip to content

Commit d1971c4

Browse files
committed
Test e2e workflow with pgs
1 parent 9916985 commit d1971c4

File tree

17 files changed

+217
-273
lines changed

17 files changed

+217
-273
lines changed

.env.example

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,20 @@ TUNS_CONSOLE_SECRET=
152152
GARAGE_CADDYFILE=./caddy/Caddyfile.garage
153153
GARAGE_METRICS_TOKEN=secret
154154
GARAGE_ADMIN_TOKEN=secret
155-
GARAGE_RPC_TOKEN=secret
155+
156+
# Garage requires a random secret for the RPC server. This was generated with:
157+
# openssl rand -hex 32
158+
# A different one is used in production.
159+
GARAGE_RPC_SECRET=69da4443f998314dfda3341ceb77205316a36e2323599f6932fba2c21185c7ef
160+
161+
# For the garage webui
162+
API_ADMIN_KEY=${GARAGE_ADMIN_TOKEN}
163+
API_BASE_URL="http://garage:3903"
164+
S3_ENDPOINT_URL="http://garage:3900"
165+
166+
# Configure the storage type. These are dev only fixtures.
167+
STORAGE_TYPE=garage
168+
GARAGE_URL=http://garage:3900
169+
GARAGE_ADMIN_URL=http://garage:3903
170+
GARAGE_ROOT_USER=GK03c0bc4880d00a540d929ee9
171+
GARAGE_ROOT_PASSWORD=88a01d80ba28c04f2c7013445506bdcd883cb44aa32e63731bcd42971697f171

Makefile

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,23 @@ restore:
153153

154154
dev-db-up:
155155
$(DOCKER_CMD) compose --profile db up -d
156-
sleep 5
156+
sleep 2
157+
make create migrate
157158
make setup-dev-garage
158-
.PHONY: db-up
159+
.PHONY: dev-db-up
159160

160161
dev-db-down:
161162
$(DOCKER_CMD) compose --profile db down
162-
.PHONY: db-down
163+
.PHONY: dev-db-down
164+
165+
setup-dev-db:
166+
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage layout assign -z ash -c 10G $(shell $(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage status 2>&1 | grep -A 1 ID | tail -n1 | awk '{print $$1}')
167+
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage layout apply --version 1
168+
.PHONY: setup-dev-db
163169

164170
setup-dev-garage:
165171
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage layout assign -z ash -c 10G $(shell $(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage status 2>&1 | grep -A 1 ID | tail -n1 | awk '{print $$1}')
166172
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage layout apply --version 1
167-
.PHONY: setup-garage
173+
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage key import --yes -n dev GK03c0bc4880d00a540d929ee9 88a01d80ba28c04f2c7013445506bdcd883cb44aa32e63731bcd42971697f171
174+
$(DOCKER_CMD) exec $(GARAGE_CONTAINER) /garage key allow --create-bucket dev
175+
.PHONY: setup-dev-garage

docker-compose.override.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ services:
1818
- "3900-3904:3900-3904"
1919
tmpfs:
2020
- /data
21+
garage-webui:
22+
env_file:
23+
- .env.example
24+
ports:
25+
- "3909:3909"
2126
imgproxy:
2227
env_file:
2328
- .env.example

docker-compose.prod.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ services:
5353
- .env.prod
5454
volumes:
5555
- ./data/garage-data:/data
56+
garage-webui:
57+
env_file:
58+
- .env.prod
5659
pipemgr:
5760
env_file:
5861
- .env.prod

docker-compose.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,16 @@ services:
1313
- db
1414
- all
1515
garage:
16-
image: dxflrs/garage:v2.0.0-beta1
16+
image: dxflrs/garage:v1.1.0
17+
restart: always
18+
volumes:
19+
- ./garage/config.toml:/etc/garage.toml:ro
20+
profiles:
21+
- db
22+
- storage
23+
- all
24+
garage-webui:
25+
image: khairul169/garage-webui:latest
1726
restart: always
1827
volumes:
1928
- ./garage/config.toml:/etc/garage.toml:ro

garage/config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ replication_factor = 1
88
rpc_bind_addr = "[::]:3901"
99

1010
[s3_api]
11-
s3_region = "ash"
11+
s3_region = "us-east-1"
1212
api_bind_addr = "[::]:3900"
1313

1414
[admin]

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ toolchain go1.24.0
2020

2121
// replace git.sr.ht/~rockorager/vaxis => ../../vaxis
2222

23-
replace git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang => github.com/picosh/garage-admin-sdk-golang v0.0.0-20250429033256-41fd1604e078
23+
replace git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang => github.com/picosh/garage-admin-sdk-golang v0.0.0-20250504231927-e10d3d8e125c
2424

2525
require (
2626
git.deuxfleurs.fr/garage-sdk/garage-admin-sdk-golang v0.0.0-20250305093803-0806b8639fd3
@@ -282,7 +282,7 @@ require (
282282
golang.org/x/image v0.25.0 // indirect
283283
golang.org/x/mod v0.24.0 // indirect
284284
golang.org/x/net v0.38.0 // indirect
285-
golang.org/x/oauth2 v0.28.0 // indirect
285+
golang.org/x/oauth2 v0.29.0 // indirect
286286
golang.org/x/sync v0.12.0 // indirect
287287
golang.org/x/sys v0.31.0 // indirect
288288
golang.org/x/term v0.30.0 // indirect
@@ -293,6 +293,7 @@ require (
293293
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
294294
google.golang.org/grpc v1.71.0 // indirect
295295
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
296+
gopkg.in/validator.v2 v2.0.1 // indirect
296297
gopkg.in/yaml.v3 v3.0.1 // indirect
297298
howett.net/plist v1.0.1 // indirect
298299
mvdan.cc/xurls/v2 v2.6.0 // indirect

go.sum

Lines changed: 6 additions & 233 deletions
Large diffs are not rendered by default.

pkg/apps/pgs/uploader.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package pgs
22

33
import (
4-
"bytes"
54
"context"
65
"fmt"
76
"io"
@@ -325,12 +324,12 @@ func (h *UploadAssetHandler) Write(s *pssh.SSHServerConnSession, entry *sendutil
325324
}
326325

327326
if entry.Mode.IsDir() {
328-
_, _, err := h.Cfg.Storage.PutObject(
327+
_, err := h.Cfg.Storage.PutEmptyObject(
329328
bucket,
330329
path.Join(shared.GetAssetFileName(entry), "._pico_keep_dir"),
331-
bytes.NewReader([]byte{}),
332330
entry,
333331
)
332+
334333
return "", err
335334
}
336335

@@ -399,9 +398,14 @@ func (h *UploadAssetHandler) Write(s *pssh.SSHServerConnSession, entry *sendutil
399398
sizeRemaining = min(sizeRemaining, specialFileMax)
400399
}
401400

401+
var r io.Reader
402+
if entry.Reader != nil {
403+
r = utils.NewMaxBytesReader(data.Reader, int64(sizeRemaining))
404+
}
405+
402406
fsize, err := h.writeAsset(
403407
s,
404-
utils.NewMaxBytesReader(data.Reader, int64(sizeRemaining)),
408+
r,
405409
data,
406410
)
407411
if err != nil {
@@ -494,16 +498,17 @@ func (h *UploadAssetHandler) Delete(s *pssh.SSHServerConnSession, entry *senduti
494498
})
495499

496500
if len(sibs) == 0 {
497-
_, _, err := h.Cfg.Storage.PutObject(
501+
_, err := h.Cfg.Storage.PutEmptyObject(
498502
bucket,
499503
filepath.Join(pathDir, "._pico_keep_dir"),
500-
bytes.NewReader([]byte{}),
501504
entry,
502505
)
506+
503507
if err != nil {
504508
return err
505509
}
506510
}
511+
507512
err = h.Cfg.Storage.DeleteObject(bucket, assetFilepath)
508513

509514
surrogate := getSurrogateKey(user.Name, projectName)
@@ -551,12 +556,24 @@ func (h *UploadAssetHandler) writeAsset(s *pssh.SSHServerConnSession, reader io.
551556
"filename", assetFilepath,
552557
)
553558

554-
_, fsize, err := h.Cfg.Storage.PutObject(
555-
data.Bucket,
556-
assetFilepath,
557-
reader,
558-
data.FileEntry,
559-
)
559+
var fsize int64
560+
var err error
561+
562+
if reader == nil {
563+
_, err = h.Cfg.Storage.PutEmptyObject(
564+
data.Bucket,
565+
assetFilepath,
566+
data.FileEntry,
567+
)
568+
} else {
569+
_, fsize, err = h.Cfg.Storage.PutObject(
570+
data.Bucket,
571+
assetFilepath,
572+
reader,
573+
data.FileEntry,
574+
)
575+
}
576+
560577
return fsize, err
561578
}
562579

pkg/pobj/storage/fs.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,27 @@ func (s *StorageFS) PutObject(bucket Bucket, fpath string, contents io.Reader, e
145145
return loc, size, nil
146146
}
147147

148+
func (s *StorageFS) PutEmptyObject(bucket Bucket, fpath string, entry *utils.FileEntry) (string, error) {
149+
loc := filepath.Join(bucket.Path, fpath)
150+
err := os.MkdirAll(filepath.Dir(loc), os.ModePerm)
151+
if err != nil {
152+
return "", err
153+
}
154+
155+
f, err := os.OpenFile(loc, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
156+
if err != nil {
157+
return "", err
158+
}
159+
defer f.Close()
160+
161+
if entry.Mtime > 0 {
162+
uTime := time.Unix(entry.Mtime, 0)
163+
_ = os.Chtimes(loc, uTime, uTime)
164+
}
165+
166+
return loc, nil
167+
}
168+
148169
func (s *StorageFS) DeleteObject(bucket Bucket, fpath string) error {
149170
loc := filepath.Join(bucket.Path, fpath)
150171
err := os.Remove(loc)

pkg/pobj/storage/garage.go

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
type StorageGarage struct {
2525
Client *minio.Client
26+
ClientKey string
2627
Admin *garage.APIClient
2728
AdminCtx context.Context
2829
BucketCache *expirable.LRU[string, CachedBucket]
@@ -34,7 +35,7 @@ var (
3435
_ ObjectStorage = (*StorageGarage)(nil)
3536
)
3637

37-
func NewStorageGarage(logger *slog.Logger, address, user, pass, token string) (*StorageGarage, error) {
38+
func NewStorageGarage(logger *slog.Logger, address, user, pass, adminAddress, token string) (*StorageGarage, error) {
3839
endpoint, err := url.Parse(address)
3940
if err != nil {
4041
return nil, err
@@ -49,20 +50,26 @@ func NewStorageGarage(logger *slog.Logger, address, user, pass, token string) (*
4950
return nil, err
5051
}
5152

53+
adminEndpoint, err := url.Parse(adminAddress)
54+
if err != nil {
55+
return nil, err
56+
}
57+
5258
configuration := garage.NewConfiguration()
53-
configuration.Host = endpoint.Host
54-
configuration.Scheme = endpoint.Scheme
59+
configuration.Host = adminEndpoint.Host
60+
configuration.Scheme = adminEndpoint.Scheme
5561

5662
client := garage.NewAPIClient(configuration)
5763
ctx := context.WithValue(context.Background(), garage.ContextAccessToken, token)
5864

59-
_, _, err = client.LayoutApi.GetLayout(ctx).Execute()
65+
_, _, err = client.LayoutAPI.GetLayout(ctx).Execute()
6066
if err != nil {
6167
return nil, err
6268
}
6369

6470
mini := &StorageGarage{
6571
Client: mClient,
72+
ClientKey: user,
6673
Admin: client,
6774
AdminCtx: ctx,
6875
BucketCache: expirable.NewLRU[string, CachedBucket](2048, nil, cache.CacheTimeout),
@@ -104,18 +111,41 @@ func (s *StorageGarage) UpsertBucket(name string) (Bucket, error) {
104111
return bucket, nil
105112
}
106113

107-
err = s.Client.MakeBucket(context.TODO(), name, minio.MakeBucketOptions{})
114+
createBucketRequest := garage.NewCreateBucketRequest()
115+
createBucketRequest.SetGlobalAlias(name)
116+
117+
bucketInfo, _, err := s.Admin.BucketAPI.CreateBucket(s.AdminCtx).CreateBucketRequest(*createBucketRequest).Execute()
118+
if err != nil {
119+
return bucket, err
120+
}
121+
122+
permissions := garage.NewAllowBucketKeyRequestPermissions(true, true, true)
123+
allowBucketKeyRequest := garage.NewAllowBucketKeyRequest(bucketInfo.GetId(), s.ClientKey, *permissions)
124+
125+
_, _, err = s.Admin.BucketAPI.AllowBucketKey(s.AdminCtx).AllowBucketKeyRequest(*allowBucketKeyRequest).Execute()
108126
if err != nil {
109127
return bucket, err
110128
}
111129

130+
// We can have garage enforce the quota on the bucket
131+
// bucketQuotas := garage.NewUpdateBucketRequestQuotas()
132+
// bucketQuotas.SetMaxSize()
133+
134+
// updateBucketRequest := garage.NewUpdateBucketRequest()
135+
// updateBucketRequest.SetQuotas(*bucketQuotas)
136+
137+
// _, _, err = s.Admin.BucketAPI.UpdateBucket(s.AdminCtx).Id(bucketInfo.GetId()).UpdateBucketRequest(*updateBucketRequest).Execute()
138+
// if err != nil {
139+
// return bucket, err
140+
// }
141+
112142
s.BucketCache.Remove(name)
113143

114144
return bucket, nil
115145
}
116146

117147
func (s *StorageGarage) GetBucketQuota(bucket Bucket) (uint64, error) {
118-
info, _, err := s.Admin.BucketApi.GetBucketInfo(s.AdminCtx).GlobalAlias(bucket.Name).Execute()
148+
info, _, err := s.Admin.BucketAPI.GetBucketInfo(s.AdminCtx).GlobalAlias(bucket.Name).Execute()
119149
if err != nil {
120150
return 0, err
121151
}
@@ -124,15 +154,11 @@ func (s *StorageGarage) GetBucketQuota(bucket Bucket) (uint64, error) {
124154
return 0, fmt.Errorf("bucket %s not found", bucket.Name)
125155
}
126156

127-
if info.Quotas == nil {
128-
return 0, fmt.Errorf("bucket %s has no quota", bucket.Name)
129-
}
130-
131-
if info.Quotas.MaxSize.Get() == nil {
132-
return 0, fmt.Errorf("bucket %s has no quota", bucket.Name)
157+
if info.Bytes == nil {
158+
return 0, fmt.Errorf("bucket %s has no size", bucket.Name)
133159
}
134160

135-
return uint64(*info.Quotas.MaxSize.Get()), nil
161+
return uint64(info.GetBytes()), nil
136162
}
137163

138164
func (s *StorageGarage) ListBuckets() ([]string, error) {
@@ -242,6 +268,26 @@ func (s *StorageGarage) PutObject(bucket Bucket, fpath string, contents io.Reade
242268
return fmt.Sprintf("%s/%s", info.Bucket, info.Key), info.Size, nil
243269
}
244270

271+
func (s *StorageGarage) PutEmptyObject(bucket Bucket, fpath string, entry *utils.FileEntry) (string, error) {
272+
opts := minio.PutObjectOptions{
273+
UserMetadata: map[string]string{
274+
"Mtime": fmt.Sprint(time.Now().Unix()),
275+
},
276+
}
277+
278+
if entry.Mtime > 0 {
279+
opts.UserMetadata["Mtime"] = fmt.Sprint(entry.Mtime)
280+
}
281+
282+
info, err := s.Client.PutObject(context.TODO(), bucket.Name, fpath, nil, 0, opts)
283+
284+
if err != nil {
285+
return "", err
286+
}
287+
288+
return fmt.Sprintf("%s/%s", info.Bucket, info.Key), nil
289+
}
290+
245291
func (s *StorageGarage) DeleteObject(bucket Bucket, fpath string) error {
246292
err := s.Client.RemoveObject(context.TODO(), bucket.Name, fpath, minio.RemoveObjectOptions{})
247293
return err

pkg/pobj/storage/memory.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ func (s *StorageMemory) PutObject(bucket Bucket, fpath string, contents io.Reade
112112
return fmt.Sprintf("%s%s", bucket.Path, fpath), int64(len(d)), nil
113113
}
114114

115+
func (s *StorageMemory) PutEmptyObject(bucket Bucket, fpath string, entry *utils.FileEntry) (string, error) {
116+
s.mu.Lock()
117+
defer s.mu.Unlock()
118+
119+
s.storage[bucket.Path][fpath] = ""
120+
return fmt.Sprintf("%s%s", bucket.Path, fpath), nil
121+
}
122+
115123
func (s *StorageMemory) DeleteObject(bucket Bucket, fpath string) error {
116124
s.mu.Lock()
117125
defer s.mu.Unlock()

0 commit comments

Comments
 (0)