v2.1.0 0fb867d

service.go RawHistoryFolder

1// Package registry implements the container registry use case.
2package registry
3
4import (
5	"context"
6	"io"
7
8	"github.com/bnema/zerowrap"
9
10	"gordon/internal/boundaries/out"
11	"gordon/internal/domain"
12)
13
14// Service implements the RegistryService interface.
15type Service struct {
16	blobStorage     out.BlobStorage
17	manifestStorage out.ManifestStorage
18	eventBus        out.EventPublisher
19}
20
21// NewService creates a new registry service.
22func NewService(
23	blobStorage out.BlobStorage,
24	manifestStorage out.ManifestStorage,
25	eventBus out.EventPublisher,
26) *Service {
27	return &Service{
28		blobStorage:     blobStorage,
29		manifestStorage: manifestStorage,
30		eventBus:        eventBus,
31	}
32}
33
34// GetManifest retrieves a manifest by name and reference.
35func (s *Service) GetManifest(ctx context.Context, name, reference string) (*domain.Manifest, error) {
36	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
37		zerowrap.FieldLayer:   "usecase",
38		zerowrap.FieldUseCase: "GetManifest",
39		"name":                name,
40		"reference":           reference,
41	})
42	log := zerowrap.FromCtx(ctx)
43
44	data, contentType, err := s.manifestStorage.GetManifest(name, reference)
45	if err != nil {
46		return nil, log.WrapErr(err, "failed to get manifest")
47	}
48
49	return &domain.Manifest{
50		Name:        name,
51		Reference:   reference,
52		ContentType: contentType,
53		Data:        data,
54	}, nil
55}
56
57// PutManifest stores a manifest.
58func (s *Service) PutManifest(ctx context.Context, manifest *domain.Manifest) error {
59	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
60		zerowrap.FieldLayer:   "usecase",
61		zerowrap.FieldUseCase: "PutManifest",
62		"name":                manifest.Name,
63		"reference":           manifest.Reference,
64	})
65	log := zerowrap.FromCtx(ctx)
66
67	if err := s.manifestStorage.PutManifest(manifest.Name, manifest.Reference, manifest.ContentType, manifest.Data); err != nil {
68		return log.WrapErr(err, "failed to store manifest")
69	}
70
71	// Publish image pushed event
72	if s.eventBus != nil {
73		if err := s.eventBus.Publish(domain.EventImagePushed, domain.ImagePushedPayload{
74			Name:        manifest.Name,
75			Reference:   manifest.Reference,
76			Manifest:    manifest.Data,
77			Annotations: manifest.Annotations,
78		}); err != nil {
79			log.Warn().Err(err).Msg("failed to publish image pushed event")
80		}
81	}
82
83	log.Info().Msg("manifest stored")
84	return nil
85}
86
87// DeleteManifest removes a manifest.
88func (s *Service) DeleteManifest(ctx context.Context, name, reference string) error {
89	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
90		zerowrap.FieldLayer:   "usecase",
91		zerowrap.FieldUseCase: "DeleteManifest",
92		"name":                name,
93		"reference":           reference,
94	})
95	log := zerowrap.FromCtx(ctx)
96
97	if err := s.manifestStorage.DeleteManifest(name, reference); err != nil {
98		return log.WrapErr(err, "failed to delete manifest")
99	}
100
101	log.Info().Msg("manifest deleted")
102	return nil
103}
104
105// GetBlob retrieves a blob by digest.
106func (s *Service) GetBlob(ctx context.Context, digest string) (io.ReadCloser, error) {
107	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
108		zerowrap.FieldLayer:   "usecase",
109		zerowrap.FieldUseCase: "GetBlob",
110		"digest":              digest,
111	})
112	log := zerowrap.FromCtx(ctx)
113
114	reader, err := s.blobStorage.GetBlob(digest)
115	if err != nil {
116		return nil, log.WrapErr(err, "failed to get blob")
117	}
118
119	return reader, nil
120}
121
122// PutBlob stores a blob with the given digest.
123func (s *Service) PutBlob(ctx context.Context, digest string, data io.Reader, size int64) error {
124	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
125		zerowrap.FieldLayer:   "usecase",
126		zerowrap.FieldUseCase: "PutBlob",
127		"digest":              digest,
128		zerowrap.FieldSize:    size,
129	})
130	log := zerowrap.FromCtx(ctx)
131
132	if err := s.blobStorage.PutBlob(digest, data, size); err != nil {
133		return log.WrapErr(err, "failed to store blob")
134	}
135
136	log.Info().Msg("blob stored")
137	return nil
138}
139
140// BlobExists checks if a blob exists.
141func (s *Service) BlobExists(_ context.Context, digest string) bool {
142	return s.blobStorage.BlobExists(digest)
143}
144
145// StartUpload starts a new blob upload.
146func (s *Service) StartUpload(ctx context.Context, name string) (string, error) {
147	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
148		zerowrap.FieldLayer:   "usecase",
149		zerowrap.FieldUseCase: "StartUpload",
150		"name":                name,
151	})
152	log := zerowrap.FromCtx(ctx)
153
154	uuid, err := s.blobStorage.StartBlobUpload(name)
155	if err != nil {
156		return "", log.WrapErr(err, "failed to start blob upload")
157	}
158
159	log.Info().Str("uuid", uuid).Msg("blob upload started")
160	return uuid, nil
161}
162
163// FinishUpload completes a blob upload.
164func (s *Service) FinishUpload(ctx context.Context, uuid, digest string) error {
165	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
166		zerowrap.FieldLayer:   "usecase",
167		zerowrap.FieldUseCase: "FinishUpload",
168		"uuid":                uuid,
169		"digest":              digest,
170	})
171	log := zerowrap.FromCtx(ctx)
172
173	if err := s.blobStorage.FinishBlobUpload(uuid, digest); err != nil {
174		return log.WrapErr(err, "failed to finish blob upload")
175	}
176
177	log.Info().Msg("blob upload finished")
178	return nil
179}
180
181// CancelUpload cancels an in-progress upload.
182func (s *Service) CancelUpload(ctx context.Context, uuid string) error {
183	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
184		zerowrap.FieldLayer:   "usecase",
185		zerowrap.FieldUseCase: "CancelUpload",
186		"uuid":                uuid,
187	})
188	log := zerowrap.FromCtx(ctx)
189
190	if err := s.blobStorage.CancelBlobUpload(uuid); err != nil {
191		return log.WrapErr(err, "failed to cancel blob upload")
192	}
193
194	log.Info().Msg("blob upload cancelled")
195	return nil
196}
197
198// ListTags returns all tags for a repository.
199func (s *Service) ListTags(ctx context.Context, name string) ([]string, error) {
200	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
201		zerowrap.FieldLayer:   "usecase",
202		zerowrap.FieldUseCase: "ListTags",
203		"name":                name,
204	})
205	log := zerowrap.FromCtx(ctx)
206
207	tags, err := s.manifestStorage.ListTags(name)
208	if err != nil {
209		return nil, log.WrapErr(err, "failed to list tags")
210	}
211
212	return tags, nil
213}
214
215// ListRepositories returns all repository names.
216func (s *Service) ListRepositories(ctx context.Context) ([]string, error) {
217	ctx = zerowrap.CtxWithFields(ctx, map[string]any{
218		zerowrap.FieldLayer:   "usecase",
219		zerowrap.FieldUseCase: "ListRepositories",
220	})
221	log := zerowrap.FromCtx(ctx)
222
223	repos, err := s.manifestStorage.ListRepositories()
224	if err != nil {
225		return nil, log.WrapErr(err, "failed to list repositories")
226	}
227
228	return repos, nil
229}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close