diff --git a/cmd/api/api/images.go b/cmd/api/api/images.go index dc003370d..8c4078de5 100644 --- a/cmd/api/api/images.go +++ b/cmd/api/api/images.go @@ -94,6 +94,39 @@ func (s *ApiService) CreateImage(ctx context.Context, request oapi.CreateImageRe return oapi.CreateImage202JSONResponse(imageToOAPI(*img)), nil } +// TagImage handles POST /images/{name}/tag. +// Note: the ResolveResource middleware also resolves {name} via GetImage before +// this handler runs, so a missing source returns the middleware's generic 404 +// body and the source is resolved twice per request. +func (s *ApiService) TagImage(ctx context.Context, request oapi.TagImageRequestObject) (oapi.TagImageResponseObject, error) { + if request.Body == nil { + return oapi.TagImage400JSONResponse{ + Code: "invalid_request", + Message: "request body is required", + }, nil + } + + img, err := s.ImageManager.TagImage(ctx, request.Name, request.Body.Target) + if err != nil { + return tagImageErrorResponse(ctx, err, request.Name, request.Body.Target), nil + } + return oapi.TagImage200JSONResponse(imageToOAPI(*img)), nil +} + +func tagImageErrorResponse(ctx context.Context, err error, source, target string) oapi.TagImageResponseObject { + switch { + case errors.Is(err, images.ErrInvalidName): + return oapi.TagImage400JSONResponse{Code: "invalid_name", Message: err.Error()} + case errors.Is(err, images.ErrNotFound): + return oapi.TagImage404JSONResponse{Code: "not_found", Message: "source image not found"} + case errors.Is(err, images.ErrImageNotReady): + return oapi.TagImage409JSONResponse{Code: "image_not_ready", Message: err.Error()} + default: + logger.FromContext(ctx).ErrorContext(ctx, "failed to tag image", "error", err, "source", source, "target", target) + return oapi.TagImage500JSONResponse{Code: "internal_error", Message: "failed to tag image"} + } +} + // GetImage gets image details by name // Note: Resolution is handled by ResolveResource middleware func (s *ApiService) GetImage(ctx context.Context, request oapi.GetImageRequestObject) (oapi.GetImageResponseObject, error) { diff --git a/cmd/api/api/images_test.go b/cmd/api/api/images_test.go index bb13a9c02..b6a1857b5 100644 --- a/cmd/api/api/images_test.go +++ b/cmd/api/api/images_test.go @@ -2,13 +2,12 @@ package api import ( "context" - "encoding/json" "fmt" - "os" "testing" "time" "github.com/kernel/hypeman/lib/images" + "github.com/kernel/hypeman/lib/images/testutil" "github.com/kernel/hypeman/lib/oapi" "github.com/kernel/hypeman/lib/paths" "github.com/stretchr/testify/assert" @@ -37,6 +36,15 @@ func (m *captureCreateImageManager) CreateImage(_ context.Context, req images.Cr return &images.Image{Name: req.Name, Digest: "sha256:test", Status: images.StatusPending, CreatedAt: time.Now()}, nil } +type tagImageErrManager struct { + images.Manager + err error +} + +func (m tagImageErrManager) TagImage(context.Context, string, string) (*images.Image, error) { + return nil, m.err +} + func TestCreateImage_MapsBorrowedCredentials(t *testing.T) { t.Parallel() @@ -505,28 +513,95 @@ func seedReadyDigestOnlyImage(t *testing.T, svc *ApiService, imageRef string, im require.NoError(t, err) require.True(t, ref.IsDigest(), "test helper expects a digest reference") - p := paths.New(svc.Config.DataDir) - digestDir := p.ImageDigestDir(ref.Repository(), ref.DigestHex()) - require.NoError(t, os.MkdirAll(digestDir, 0o755)) - require.NoError(t, os.WriteFile(p.ImageDigestPath(ref.Repository(), ref.DigestHex()), []byte("rootfs"), 0o644)) - - meta := struct { - Name string `json:"name"` - Digest string `json:"digest"` - Status string `json:"status"` - SizeBytes int64 `json:"size_bytes"` - Tags map[string]string `json:"tags,omitempty"` - CreatedAt time.Time `json:"created_at"` + testutil.SeedReadyImage(t, paths.New(svc.Config.DataDir), testutil.Seed{ + Repository: ref.Repository(), + DigestHex: ref.DigestHex(), + Name: imageRef, + Tags: imageTags, + }) +} + +func TestTagImage_ErrorStatusMapping(t *testing.T) { + t.Parallel() + + cases := []struct { + name string + err error + want oapi.TagImageResponseObject }{ - Name: imageRef, - Digest: "sha256:" + ref.DigestHex(), - Status: "ready", - SizeBytes: int64(len("rootfs")), - Tags: imageTags, - CreatedAt: time.Now().UTC(), + { + name: "invalid name -> 400", + err: fmt.Errorf("tag: %w", images.ErrInvalidName), + want: oapi.TagImage400JSONResponse{Code: "invalid_name", Message: "tag: invalid image name"}, + }, + { + name: "not found -> 404", + err: fmt.Errorf("tag: %w", images.ErrNotFound), + want: oapi.TagImage404JSONResponse{Code: "not_found", Message: "source image not found"}, + }, + { + name: "not ready -> 409", + err: fmt.Errorf("tag: %w", images.ErrImageNotReady), + want: oapi.TagImage409JSONResponse{Code: "image_not_ready", Message: "tag: image is not ready"}, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + svc := &ApiService{ImageManager: tagImageErrManager{err: tc.err}} + resp, err := svc.TagImage(ctx(), oapi.TagImageRequestObject{ + Name: "docker.io/library/alpine:latest", + Body: &oapi.TagImageRequest{Target: "docker.io/library/alpine:stable"}, + }) + require.NoError(t, err) + require.Equal(t, tc.want, resp) + }) } +} + +func TestTagImage_MissingBody(t *testing.T) { + t.Parallel() + + svc := &ApiService{ImageManager: tagImageErrManager{}} + resp, err := svc.TagImage(ctx(), oapi.TagImageRequestObject{ + Name: "docker.io/library/alpine:latest", + }) + require.NoError(t, err) + require.IsType(t, oapi.TagImage400JSONResponse{}, resp) +} + +// seedReadyContentImage writes a ready image into the shared content layout +// plus a repository tag reference, without pulling from a registry. +func seedReadyContentImage(t *testing.T, svc *ApiService, repository, tag, digestHex string) { + t.Helper() + testutil.SeedReadyImage(t, paths.New(svc.Config.DataDir), testutil.Seed{ + Repository: repository, + Tag: tag, + DigestHex: digestHex, + Content: true, + }) +} + +func TestTagImage_Success(t *testing.T) { + t.Parallel() + svc := newTestService(t) + + const repository = "docker.io/library/alpine" + const digestHex = "ab12cd34ab12cd34ab12cd34ab12cd34ab12cd34ab12cd34ab12cd34ab12cd34" + seedReadyContentImage(t, svc, repository, "latest", digestHex) + + resp, err := svc.TagImage(ctx(), oapi.TagImageRequestObject{ + Name: repository + ":latest", + Body: &oapi.TagImageRequest{Target: "registry.example/apps/alpine:v1"}, + }) + require.NoError(t, err) + okResp, ok := resp.(oapi.TagImage200JSONResponse) + require.True(t, ok, "expected 200 response, got %T", resp) + require.Equal(t, "registry.example/apps/alpine:v1", okResp.Name) + require.Equal(t, "sha256:"+digestHex, okResp.Digest) - data, err := json.Marshal(meta) + // The new tag resolves through GetImage. + img, err := svc.ImageManager.GetImage(ctx(), "registry.example/apps/alpine:v1") require.NoError(t, err) - require.NoError(t, os.WriteFile(p.ImageMetadata(ref.Repository(), ref.DigestHex()), data, 0o644)) + require.Equal(t, images.StatusReady, img.Status) } diff --git a/cmd/api/main_test.go b/cmd/api/main_test.go index 34dbba428..ef88b586b 100644 --- a/cmd/api/main_test.go +++ b/cmd/api/main_test.go @@ -252,6 +252,50 @@ func (h *testImageHandler) GetImage(w http.ResponseWriter, r *http.Request, name } } +func TestTagImageRoute_URLDecodedName(t *testing.T) { + // Verifies the generated POST /images/{name}/tag route exists and that the + // URL-encoded source name is decoded before reaching the handler. + r := chi.NewRouter() + + var receivedName string + handler := &testTagImageHandler{ + tagImage: func(w http.ResponseWriter, r *http.Request, name string) { + receivedName = name + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"name":"` + name + `","digest":"sha256:test","status":"ready","created_at":"2026-01-01T00:00:00Z"}`)) + }, + } + + oapi.HandlerFromMux(handler, r) + + token, err := generateValidJWT("user-123") + require.NoError(t, err) + + body := bytes.NewReader([]byte(`{"target":"docker.io/library/alpine:stable"}`)) + req := httptest.NewRequest(http.MethodPost, "/images/docker.io%2Flibrary%2Falpine%3Alatest/tag", body) + req.Header.Set("Authorization", "Bearer "+token) + req.Header.Set("Content-Type", "application/json") + + w := httptest.NewRecorder() + r.ServeHTTP(w, req) + + require.Equal(t, http.StatusOK, w.Code, "body: %s", w.Body.String()) + assert.Equal(t, "docker.io/library/alpine:latest", receivedName) +} + +// testTagImageHandler implements oapi.ServerInterface with just TagImage for testing +type testTagImageHandler struct { + oapi.Unimplemented + tagImage func(w http.ResponseWriter, r *http.Request, name string) +} + +func (h *testTagImageHandler) TagImage(w http.ResponseWriter, r *http.Request, name string) { + if h.tagImage != nil { + h.tagImage(w, r, name) + } +} + func TestImageNameWithSlashes_URLEncoding(t *testing.T) { // This test verifies how chi router handles image names with slashes. // Image names like "docker.io/onkernel/chromium-headful:latest" contain slashes diff --git a/lib/builds/manager_test.go b/lib/builds/manager_test.go index 7be58b0fc..a947090a2 100644 --- a/lib/builds/manager_test.go +++ b/lib/builds/manager_test.go @@ -369,6 +369,19 @@ func (m *mockImageManager) DeleteImage(ctx context.Context, name string) error { return nil } +func (m *mockImageManager) TagImage(ctx context.Context, source, target string) (*images.Image, error) { + m.mu.Lock() + defer m.mu.Unlock() + img, ok := m.images[source] + if !ok { + return nil, images.ErrNotFound + } + tagged := *img + tagged.Name = target + m.images[target] = &tagged + return &tagged, nil +} + func (m *mockImageManager) RecoverInterruptedBuilds() {} func (m *mockImageManager) TotalImageBytes(ctx context.Context) (int64, error) { diff --git a/lib/images/errors.go b/lib/images/errors.go index 6a63741f0..47be5032c 100644 --- a/lib/images/errors.go +++ b/lib/images/errors.go @@ -17,6 +17,7 @@ var ( ErrNotFound = errors.New("image not found") ErrInvalidName = errors.New("invalid image name") ErrInvalidPlatform = errors.New("invalid platform") + ErrImageNotReady = errors.New("image is not ready") // ErrPlatformNotAvailable means the requested platform is well-formed but the // image's manifest index does not publish a matching variant. Unlike // ErrInvalidPlatform (bad user syntax), the platform itself is valid; the diff --git a/lib/images/manager.go b/lib/images/manager.go index f89fb6678..06e6cbbe0 100644 --- a/lib/images/manager.go +++ b/lib/images/manager.go @@ -46,6 +46,10 @@ type Manager interface { // Unlike CreateImage, it does not resolve from a remote registry. ImportLocalImage(ctx context.Context, repo, reference, digest string) (*Image, error) GetImage(ctx context.Context, name string) (*Image, error) + // TagImage creates or updates a local tag pointing at an existing ready + // image, without pulling or reconverting. Source and target may be in + // different repositories. + TagImage(ctx context.Context, source, target string) (*Image, error) DeleteImage(ctx context.Context, name string) error RecoverInterruptedBuilds() // TotalImageBytes returns the total size of all ready images on disk. @@ -768,23 +772,7 @@ func (m *manager) GetImage(ctx context.Context, name string) (*Image, error) { return nil, fmt.Errorf("%w: %s", ErrInvalidName, err.Error()) } - repository := ref.Repository() - - var digestHex string - if ref.IsDigest() { - // Direct digest lookup - digestHex = ref.DigestHex() - } else { - // Tag lookup - resolve symlink - tag := ref.Tag() - d, err := resolveTag(m.paths, repository, tag) - if err != nil { - return nil, err - } - digestHex = d - } - - meta, err := readMetadata(m.paths, repository, digestHex) + _, meta, err := resolveRefMetadata(m.paths, ref) if err != nil { return nil, err } diff --git a/lib/images/storage.go b/lib/images/storage.go index 2e3430851..9c1c79521 100644 --- a/lib/images/storage.go +++ b/lib/images/storage.go @@ -240,6 +240,24 @@ func readMetadata(p *paths.Paths, repository, digestHex string) (*imageMetadata, return readMetadataAt(resolveImageLayout(p, repository, digestHex)) } +// resolveRefMetadata resolves a digest reference directly or a tag reference +// through its symlink, then reads the metadata it points at. +func resolveRefMetadata(p *paths.Paths, ref *NormalizedRef) (string, *imageMetadata, error) { + digestHex := ref.DigestHex() + if !ref.IsDigest() { + var err error + digestHex, err = resolveTag(p, ref.Repository(), ref.Tag()) + if err != nil { + return "", nil, err + } + } + meta, err := readMetadata(p, ref.Repository(), digestHex) + if err != nil { + return "", nil, err + } + return digestHex, meta, nil +} + func readContentMetadata(p *paths.Paths, digestHex string) (*imageMetadata, error) { return readMetadataAt(contentLayout(p, digestHex)) } @@ -322,6 +340,12 @@ func promoteLegacyTags(p *paths.Paths, repository, digestHex string) error { if err != nil || target != digestHex { continue } + // resolveTag validated that content-relative links resolve to this + // digest's content dir, so only legacy links (bare digest target) + // still need restaging. + if raw, err := os.Readlink(tagSymlinkPath(p, repository, tag)); err == nil && raw != digestHex { + continue + } ref, err := stageTagSymlink(p, repository, tag, digestHex) if err != nil { return fmt.Errorf("stage legacy tag %s: %w", tag, err) diff --git a/lib/images/tag.go b/lib/images/tag.go new file mode 100644 index 000000000..2d3e184e2 --- /dev/null +++ b/lib/images/tag.go @@ -0,0 +1,95 @@ +package images + +import ( + "context" + "errors" + "fmt" + "log/slog" +) + +// TagImage creates a ready-image tag without pulling or converting content. +// Cross-repository tags promote legacy content into the shared layout. A +// failed call leaves no side effects: the target tag's generation is only +// bumped after the new tag is on disk, so pending pulls that claimed the +// target tag keep their claim. When the target previously pointed at +// different content, that digest is collected after the new tag is live; +// cleanup failures are logged and do not fail the call, since the tag is +// already installed at that point. +// +// Promotion deliberately runs before the symlink install, so a symlink +// failure after a cross-repo promotion leaves the content promoted with no +// target tag. That state is gc-consistent (unreferenced content is +// collected) and retry is idempotent (promoteImageToContent short-circuits +// on ready content), which beats rolling back a completed promotion. +func (m *manager) TagImage(ctx context.Context, source, target string) (*Image, error) { + sourceRef, targetRef, err := parseTagReferences(source, target) + if err != nil { + return nil, err + } + + m.createMu.Lock() + defer m.createMu.Unlock() + + previousDigest, err := resolveTag(m.paths, targetRef.Repository(), targetRef.Tag()) + if errors.Is(err, ErrNotFound) { + previousDigest = "" + } else if err != nil { + return nil, fmt.Errorf("resolve existing target tag: %w", err) + } + + digestHex, meta, err := m.readyTagImage(sourceRef) + if err != nil { + return nil, err + } + if sourceRef.Repository() != targetRef.Repository() { + if err := promoteImageToContent(m.paths, sourceRef.Repository(), digestHex, meta); err != nil { + return nil, fmt.Errorf("promote image to content: %w", err) + } + } + if err := createTagSymlink(m.paths, targetRef.Repository(), targetRef.Tag(), digestHex); err != nil { + return nil, fmt.Errorf("create image tag: %w", err) + } + // Unlike updateExistingReference, which bumps the generation before + // installing the symlink, the bump happens after the install here so a + // failed tag call cannot invalidate a pending pull's claim on the target + // tag (pinned by TestTagImageFailureLeavesNoSideEffects). + m.nextTagGeneration(targetRef.Repository(), targetRef.Tag()) + m.cleanupReplacedTag(targetRef, previousDigest, digestHex) + + return meta.toImageFor(targetRef.String()), nil +} + +func (m *manager) cleanupReplacedTag(ref *NormalizedRef, previousDigest, digestHex string) { + if previousDigest != "" && previousDigest != digestHex { + if err := removeDigestIfUnreferenced(m.paths, ref.Repository(), previousDigest, true); err != nil { + slog.Warn("failed to collect replaced image content", "repository", ref.Repository(), "digest", previousDigest, "error", err) + } + } + m.refreshDiskUsageTotals() +} + +func parseTagReferences(source, target string) (*NormalizedRef, *NormalizedRef, error) { + sourceRef, err := ParseNormalizedRef(source) + if err != nil { + return nil, nil, fmt.Errorf("%w: invalid source reference: %s", ErrInvalidName, err) + } + targetRef, err := ParseNormalizedRef(target) + if err != nil { + return nil, nil, fmt.Errorf("%w: invalid target reference: %s", ErrInvalidName, err) + } + if targetRef.IsDigest() { + return nil, nil, fmt.Errorf("%w: target must be a tag reference, not a digest", ErrInvalidName) + } + return sourceRef, targetRef, nil +} + +func (m *manager) readyTagImage(ref *NormalizedRef) (string, *imageMetadata, error) { + digestHex, meta, err := resolveRefMetadata(m.paths, ref) + if err != nil { + return "", nil, err + } + if meta.Status != StatusReady { + return "", nil, fmt.Errorf("%w: %s", ErrImageNotReady, meta.Status) + } + return digestHex, meta, nil +} diff --git a/lib/images/tag_test.go b/lib/images/tag_test.go new file mode 100644 index 000000000..cf76b9802 --- /dev/null +++ b/lib/images/tag_test.go @@ -0,0 +1,179 @@ +package images + +import ( + "context" + "os" + "testing" + "time" + + "github.com/kernel/hypeman/lib/images/testutil" + "github.com/kernel/hypeman/lib/paths" + "github.com/stretchr/testify/require" +) + +func seedContent(t *testing.T, p *paths.Paths, repository, tag, digestHex string) { + t.Helper() + testutil.SeedReadyImage(t, p, testutil.Seed{Repository: repository, Tag: tag, DigestHex: digestHex, Content: true}) +} + +func seedLegacy(t *testing.T, p *paths.Paths, repository, tag, digestHex string) { + t.Helper() + testutil.SeedReadyImage(t, p, testutil.Seed{Repository: repository, Tag: tag, DigestHex: digestHex}) +} + +func newTagTestCase(t *testing.T) (*paths.Paths, *manager, string) { + t.Helper() + p := paths.New(t.TempDir()) + return p, &manager{paths: p, tagGenerations: make(map[string]uint64)}, "docker.io/library/alpine" +} + +func requireTagResolvesTo(t *testing.T, p *paths.Paths, repository, tag, digest string) { + t.Helper() + resolved, err := resolveTag(p, repository, tag) + require.NoError(t, err) + require.Equal(t, digest, resolved) +} + +func tagImage(t *testing.T, m *manager, source, target, digest string) { + t.Helper() + img, err := m.TagImage(context.Background(), source, target) + require.NoError(t, err) + require.Equal(t, target, img.Name) + require.Equal(t, "sha256:"+digest, img.Digest) +} + +func TestTagImageAliasesReadyImage(t *testing.T) { + cases := []struct { + name, source, target, digest string + }{ + {"same repository", "docker.io/library/alpine:latest", "docker.io/library/alpine:stable", "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1"}, + {"digest source", "docker.io/library/alpine@sha256:b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2", "docker.io/library/alpine:pinned", "b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + p, m, repository := newTagTestCase(t) + seedContent(t, p, repository, "latest", tc.digest) + tagImage(t, m, tc.source, tc.target, tc.digest) + targetRef, err := ParseNormalizedRef(tc.target) + require.NoError(t, err) + requireTagResolvesTo(t, p, targetRef.Repository(), targetRef.Tag(), tc.digest) + }) + } +} + +func TestTagImageSameRepositoryDeletesContent(t *testing.T) { + p, m, repository := newTagTestCase(t) + digest := "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + seedContent(t, p, repository, "latest", digest) + + tagImage(t, m, repository+":latest", repository+":stable", digest) + requireTagResolvesTo(t, p, repository, "stable", digest) + + require.NoError(t, m.DeleteImage(context.Background(), repository+":latest")) + _, err := os.Stat(p.ImageContentDir(digest)) + require.NoError(t, err) + require.NoError(t, m.DeleteImage(context.Background(), repository+":stable")) + _, err = os.Stat(p.ImageContentDir(digest)) + require.True(t, os.IsNotExist(err), "content must be removed once unreferenced") +} + +func TestTagImageLegacyLayoutSource(t *testing.T) { + p, m, repository := newTagTestCase(t) + digest := "d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8d8" + seedLegacy(t, p, repository, "latest", digest) + + tagImage(t, m, repository+":latest", repository+":v2", digest) + requireTagResolvesTo(t, p, repository, "v2", digest) +} + +func TestTagImageCrossRepository(t *testing.T) { + p, m, sourceRepo := newTagTestCase(t) + targetRepo := "registry.example/apps/alpine" + digest := "c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3" + seedLegacy(t, p, sourceRepo, "latest", digest) + + tagImage(t, m, sourceRepo+":latest", targetRepo+":v1", digest) + data, err := os.ReadFile(p.ImageContentPath(digest)) + require.NoError(t, err) + require.Equal(t, "rootfs!", string(data)) + require.DirExists(t, p.ImageDigestDir(sourceRepo, digest)) + + for repo, tag := range map[string]string{sourceRepo: "latest", targetRepo: "v1"} { + requireTagResolvesTo(t, p, repo, tag, digest) + } + require.NoError(t, m.DeleteImage(context.Background(), sourceRepo+":latest")) + _, err = os.Stat(p.ImageContentDir(digest)) + require.NoError(t, err) + require.NoError(t, m.DeleteImage(context.Background(), targetRepo+":v1")) + _, err = os.Stat(p.ImageContentDir(digest)) + require.True(t, os.IsNotExist(err), "content must be removed once unreferenced") +} + +func TestTagImageRejectsNotReady(t *testing.T) { + p, m, repository := newTagTestCase(t) + digest := "d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4" + require.NoError(t, os.MkdirAll(p.ImageContentDir(digest), 0o755)) + require.NoError(t, writeMetadataFile(p.ImageContentMetadata(digest), &imageMetadata{ + Name: repository + ":latest", Digest: "sha256:" + digest, + Status: StatusConverting, CreatedAt: time.Now().UTC(), + })) + require.NoError(t, createTagSymlink(p, repository, "latest", digest)) + + targetRepository := "registry.example/apps/alpine" + _, err := m.TagImage(context.Background(), repository+":latest", targetRepository+":stable") + require.ErrorIs(t, err, ErrImageNotReady) + _, err = resolveTag(p, targetRepository, "stable") + require.ErrorIs(t, err, ErrNotFound) +} + +func TestTagImageSourceNotFound(t *testing.T) { + _, m, repository := newTagTestCase(t) + _, err := m.TagImage(context.Background(), repository+":missing", repository+":stable") + require.ErrorIs(t, err, ErrNotFound) +} + +func TestTagImageFailureLeavesNoSideEffects(t *testing.T) { + p, m, repository := newTagTestCase(t) + digest := "d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5" + seedContent(t, p, repository, "latest", digest) + + // Failed calls must not create the target tag or bump its generation, + // which would invalidate a pending pull's claim on the target tag. + _, err := m.TagImage(context.Background(), repository+":missing", repository+":stable") + require.ErrorIs(t, err, ErrNotFound) + _, err = resolveTag(p, repository, "stable") + require.ErrorIs(t, err, ErrNotFound) + require.Equal(t, uint64(0), m.tagGenerations[repository+":stable"]) + + tagImage(t, m, repository+":latest", repository+":stable", digest) + requireTagResolvesTo(t, p, repository, "stable", digest) +} + +func TestTagImageRejectsDigestTarget(t *testing.T) { + p, m, repository := newTagTestCase(t) + digest := "e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5" + seedContent(t, p, repository, "latest", digest) + _, err := m.TagImage(context.Background(), repository+":latest", repository+"@sha256:"+digest) + require.ErrorIs(t, err, ErrInvalidName) +} + +func TestTagImageReplacesExistingTagAndCollectsOldContent(t *testing.T) { + p, m, repository := newTagTestCase(t) + first := "f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6" + second := "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + seedContent(t, p, repository, "latest", first) + seedContent(t, p, repository, "stable", second) + + readyBytes, err := m.TotalImageBytes(context.Background()) + require.NoError(t, err) + require.Equal(t, int64(14), readyBytes) + + tagImage(t, m, repository+":latest", repository+":stable", first) + requireTagResolvesTo(t, p, repository, "stable", first) + _, err = os.Stat(p.ImageContentDir(second)) + require.ErrorIs(t, err, os.ErrNotExist) + + readyBytes, err = m.TotalImageBytes(context.Background()) + require.NoError(t, err) + require.Equal(t, int64(7), readyBytes) +} diff --git a/lib/images/testutil/testutil.go b/lib/images/testutil/testutil.go new file mode 100644 index 000000000..ebf356d2d --- /dev/null +++ b/lib/images/testutil/testutil.go @@ -0,0 +1,93 @@ +// Package testutil provides helpers for seeding on-disk image state in tests. +package testutil + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + "time" + + "github.com/kernel/hypeman/lib/paths" + "github.com/stretchr/testify/require" +) + +const seedImageContent = "rootfs!" + +// Seed describes a ready image to write directly to disk, bypassing pulls, +// builds, and conversion. +type Seed struct { + Repository string + DigestHex string + // Tag optionally creates a tag symlink pointing at the image. + Tag string + // Name overrides the metadata "name" field; it defaults to + // repository:tag, or repository@sha256:digest when Tag is empty. + Name string + // Tags records resource tags in the metadata. + Tags map[string]string + // Content writes the image into the shared content layout instead of + // the legacy per-repository digest layout. + Content bool +} + +// SeedReadyImage writes a ready image to disk per s. The metadata struct +// mirrors the unexported images.imageMetadata schema; fields left unset +// decode as zero values. +func SeedReadyImage(t testing.TB, p *paths.Paths, s Seed) { + t.Helper() + + if s.Content { + require.NoError(t, os.MkdirAll(p.ImageContentDir(s.DigestHex), 0o755)) + require.NoError(t, os.WriteFile(p.ImageContentPath(s.DigestHex), []byte(seedImageContent), 0o644)) + } else { + require.NoError(t, os.MkdirAll(p.ImageDigestDir(s.Repository, s.DigestHex), 0o755)) + require.NoError(t, os.WriteFile(p.ImageDigestPath(s.Repository, s.DigestHex), []byte(seedImageContent), 0o644)) + } + + name := s.Name + if name == "" { + if s.Tag != "" { + name = s.Repository + ":" + s.Tag + } else { + name = s.Repository + "@sha256:" + s.DigestHex + } + } + meta := struct { + Name string `json:"name"` + Digest string `json:"digest"` + Status string `json:"status"` + SizeBytes int64 `json:"size_bytes"` + Tags map[string]string `json:"tags,omitempty"` + CreatedAt time.Time `json:"created_at"` + }{ + Name: name, + Digest: "sha256:" + s.DigestHex, + Status: "ready", + SizeBytes: int64(len(seedImageContent)), + Tags: s.Tags, + CreatedAt: time.Now().UTC(), + } + data, err := json.Marshal(meta) + require.NoError(t, err) + if s.Content { + require.NoError(t, os.WriteFile(p.ImageContentMetadata(s.DigestHex), data, 0o644)) + } else { + require.NoError(t, os.WriteFile(p.ImageMetadata(s.Repository, s.DigestHex), data, 0o644)) + } + + if s.Tag == "" { + return + } + if s.Content { + linkPath := p.ImageRepositoryTagSymlink(s.Repository, s.Tag) + target, err := filepath.Rel(filepath.Dir(linkPath), p.ImageContentDir(s.DigestHex)) + require.NoError(t, err) + require.NoError(t, os.MkdirAll(filepath.Dir(linkPath), 0o755)) + require.NoError(t, os.Symlink(target, linkPath)) + } else { + linkPath := p.ImageTagSymlink(s.Repository, s.Tag) + require.NoError(t, os.MkdirAll(filepath.Dir(linkPath), 0o755)) + require.NoError(t, os.Symlink(s.DigestHex, linkPath)) + } +} diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index ef827c489..bda3c0909 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -1841,6 +1841,12 @@ type StandbyInstanceRequest struct { CompressionDelay *string `json:"compression_delay,omitempty"` } +// TagImageRequest defines model for TagImageRequest. +type TagImageRequest struct { + // Target Target OCI image reference with a tag. The local tag points to the source image without pulling it again. + Target string `json:"target"` +} + // Tags User-defined key-value tags. type Tags map[string]string @@ -2131,6 +2137,9 @@ type CreateDeviceJSONRequestBody = CreateDeviceRequest // CreateImageJSONRequestBody defines body for CreateImage for application/json ContentType. type CreateImageJSONRequestBody = CreateImageRequest +// TagImageJSONRequestBody defines body for TagImage for application/json ContentType. +type TagImageJSONRequestBody = TagImageRequest + // CreateIngressJSONRequestBody defines body for CreateIngress for application/json ContentType. type CreateIngressJSONRequestBody = CreateIngressRequest @@ -2315,6 +2324,11 @@ type ClientInterface interface { // GetImage request GetImage(ctx context.Context, name string, reqEditors ...RequestEditorFn) (*http.Response, error) + // TagImageWithBody request with any body + TagImageWithBody(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + TagImage(ctx context.Context, name string, body TagImageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // ListIngresses request ListIngresses(ctx context.Context, params *ListIngressesParams, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2755,6 +2769,30 @@ func (c *Client) GetImage(ctx context.Context, name string, reqEditors ...Reques return c.Client.Do(req) } +func (c *Client) TagImageWithBody(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTagImageRequestWithBody(c.Server, name, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) TagImage(ctx context.Context, name string, body TagImageJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTagImageRequest(c.Server, name, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) ListIngresses(ctx context.Context, params *ListIngressesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewListIngressesRequest(c.Server, params) if err != nil { @@ -4203,6 +4241,53 @@ func NewGetImageRequest(server string, name string) (*http.Request, error) { return req, nil } +// NewTagImageRequest calls the generic TagImage builder with application/json body +func NewTagImageRequest(server string, name string, body TagImageJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewTagImageRequestWithBody(server, name, "application/json", bodyReader) +} + +// NewTagImageRequestWithBody generates requests for TagImage with any type of body +func NewTagImageRequestWithBody(server string, name string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "name", runtime.ParamLocationPath, name) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/images/%s/tag", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewListIngressesRequest generates requests for ListIngresses func NewListIngressesRequest(server string, params *ListIngressesParams) (*http.Request, error) { var err error @@ -6166,6 +6251,11 @@ type ClientWithResponsesInterface interface { // GetImageWithResponse request GetImageWithResponse(ctx context.Context, name string, reqEditors ...RequestEditorFn) (*GetImageResponse, error) + // TagImageWithBodyWithResponse request with any body + TagImageWithBodyWithResponse(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TagImageResponse, error) + + TagImageWithResponse(ctx context.Context, name string, body TagImageJSONRequestBody, reqEditors ...RequestEditorFn) (*TagImageResponse, error) + // ListIngressesWithResponse request ListIngressesWithResponse(ctx context.Context, params *ListIngressesParams, reqEditors ...RequestEditorFn) (*ListIngressesResponse, error) @@ -6833,6 +6923,32 @@ func (r GetImageResponse) StatusCode() int { return 0 } +type TagImageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Image + JSON400 *Error + JSON404 *Error + JSON409 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r TagImageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r TagImageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type ListIngressesResponse struct { Body []byte HTTPResponse *http.Response @@ -8065,6 +8181,23 @@ func (c *ClientWithResponses) GetImageWithResponse(ctx context.Context, name str return ParseGetImageResponse(rsp) } +// TagImageWithBodyWithResponse request with arbitrary body returning *TagImageResponse +func (c *ClientWithResponses) TagImageWithBodyWithResponse(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TagImageResponse, error) { + rsp, err := c.TagImageWithBody(ctx, name, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseTagImageResponse(rsp) +} + +func (c *ClientWithResponses) TagImageWithResponse(ctx context.Context, name string, body TagImageJSONRequestBody, reqEditors ...RequestEditorFn) (*TagImageResponse, error) { + rsp, err := c.TagImage(ctx, name, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseTagImageResponse(rsp) +} + // ListIngressesWithResponse request returning *ListIngressesResponse func (c *ClientWithResponses) ListIngressesWithResponse(ctx context.Context, params *ListIngressesParams, reqEditors ...RequestEditorFn) (*ListIngressesResponse, error) { rsp, err := c.ListIngresses(ctx, params, reqEditors...) @@ -9463,6 +9596,60 @@ func ParseGetImageResponse(rsp *http.Response) (*GetImageResponse, error) { return response, nil } +// ParseTagImageResponse parses an HTTP response from a TagImageWithResponse call +func ParseTagImageResponse(rsp *http.Response) (*TagImageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &TagImageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Image + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON409 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseListIngressesResponse parses an HTTP response from a ListIngressesWithResponse call func ParseListIngressesResponse(rsp *http.Response) (*ListIngressesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -11413,6 +11600,9 @@ type ServerInterface interface { // Get image details // (GET /images/{name}) GetImage(w http.ResponseWriter, r *http.Request, name string) + // Create or update a local image tag + // (POST /images/{name}/tag) + TagImage(w http.ResponseWriter, r *http.Request, name string) // List ingresses // (GET /ingresses) ListIngresses(w http.ResponseWriter, r *http.Request, params ListIngressesParams) @@ -11668,6 +11858,12 @@ func (_ Unimplemented) GetImage(w http.ResponseWriter, r *http.Request, name str w.WriteHeader(http.StatusNotImplemented) } +// Create or update a local image tag +// (POST /images/{name}/tag) +func (_ Unimplemented) TagImage(w http.ResponseWriter, r *http.Request, name string) { + w.WriteHeader(http.StatusNotImplemented) +} + // List ingresses // (GET /ingresses) func (_ Unimplemented) ListIngresses(w http.ResponseWriter, r *http.Request, params ListIngressesParams) { @@ -12510,6 +12706,37 @@ func (siw *ServerInterfaceWrapper) GetImage(w http.ResponseWriter, r *http.Reque handler.ServeHTTP(w, r) } +// TagImage operation middleware +func (siw *ServerInterfaceWrapper) TagImage(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", chi.URLParam(r, "name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "name", Err: err}) + return + } + + ctx := r.Context() + + ctx = context.WithValue(ctx, BearerAuthScopes, []string{}) + + r = r.WithContext(ctx) + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.TagImage(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // ListIngresses operation middleware func (siw *ServerInterfaceWrapper) ListIngresses(w http.ResponseWriter, r *http.Request) { @@ -14066,6 +14293,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/images/{name}", wrapper.GetImage) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/images/{name}/tag", wrapper.TagImage) + }) r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/ingresses", wrapper.ListIngresses) }) @@ -15076,6 +15306,60 @@ func (response GetImage500JSONResponse) VisitGetImageResponse(w http.ResponseWri return json.NewEncoder(w).Encode(response) } +type TagImageRequestObject struct { + Name string `json:"name"` + Body *TagImageJSONRequestBody +} + +type TagImageResponseObject interface { + VisitTagImageResponse(w http.ResponseWriter) error +} + +type TagImage200JSONResponse Image + +func (response TagImage200JSONResponse) VisitTagImageResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type TagImage400JSONResponse Error + +func (response TagImage400JSONResponse) VisitTagImageResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type TagImage404JSONResponse Error + +func (response TagImage404JSONResponse) VisitTagImageResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type TagImage409JSONResponse Error + +func (response TagImage409JSONResponse) VisitTagImageResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(409) + + return json.NewEncoder(w).Encode(response) +} + +type TagImage500JSONResponse Error + +func (response TagImage500JSONResponse) VisitTagImageResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type ListIngressesRequestObject struct { Params ListIngressesParams } @@ -16974,6 +17258,9 @@ type StrictServerInterface interface { // Get image details // (GET /images/{name}) GetImage(ctx context.Context, request GetImageRequestObject) (GetImageResponseObject, error) + // Create or update a local image tag + // (POST /images/{name}/tag) + TagImage(ctx context.Context, request TagImageRequestObject) (TagImageResponseObject, error) // List ingresses // (GET /ingresses) ListIngresses(ctx context.Context, request ListIngressesRequestObject) (ListIngressesResponseObject, error) @@ -17689,6 +17976,39 @@ func (sh *strictHandler) GetImage(w http.ResponseWriter, r *http.Request, name s } } +// TagImage operation middleware +func (sh *strictHandler) TagImage(w http.ResponseWriter, r *http.Request, name string) { + var request TagImageRequestObject + + request.Name = name + + var body TagImageJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.TagImage(ctx, request.(TagImageRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "TagImage") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(TagImageResponseObject); ok { + if err := validResponse.VisitTagImageResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // ListIngresses operation middleware func (sh *strictHandler) ListIngresses(w http.ResponseWriter, r *http.Request, params ListIngressesParams) { var request ListIngressesRequestObject @@ -18850,385 +19170,387 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/lib/scopes/scopes.go b/lib/scopes/scopes.go index 4773437a0..9d078c1fb 100644 --- a/lib/scopes/scopes.go +++ b/lib/scopes/scopes.go @@ -229,10 +229,11 @@ var RouteScopes = map[string]Scope{ "POST /resources/memory/reclaim": ResourceWrite, // Images - "GET /images": ImageRead, - "POST /images": ImageWrite, - "DELETE /images/{name}": ImageDelete, - "GET /images/{name}": ImageRead, + "GET /images": ImageRead, + "POST /images": ImageWrite, + "POST /images/{name}/tag": ImageWrite, + "DELETE /images/{name}": ImageDelete, + "GET /images/{name}": ImageRead, // Pushes (outbound image pushes to remote registries) "POST /pushes": ImageWrite, diff --git a/openapi.yaml b/openapi.yaml index d6016524d..3b5585ec0 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -1290,6 +1290,15 @@ components: tags: $ref: "#/components/schemas/Tags" + TagImageRequest: + type: object + required: [target] + properties: + target: + type: string + description: Target OCI image reference with a tag. The local tag points to the source image without pulling it again. + example: docker.io/library/nginx:stable + Image: type: object required: [name, digest, status, created_at] @@ -2640,6 +2649,63 @@ paths: schema: $ref: "#/components/schemas/Error" + /images/{name}/tag: + post: + summary: Create or update a local image tag + operationId: tagImage + security: + - bearerAuth: [] + parameters: + - name: name + in: path + required: true + schema: + type: string + description: URL-encoded source image name or digest + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/TagImageRequest" + responses: + 200: + description: Image tag created + content: + application/json: + schema: + $ref: "#/components/schemas/Image" + 400: + description: Invalid source or target image reference + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 404: + description: Source image not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 409: + description: Source image is not ready + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 401: + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + 500: + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /images/{name}: get: summary: Get image details diff --git a/stainless.yaml b/stainless.yaml index 0d95928b2..dbd40fbc9 100644 --- a/stainless.yaml +++ b/stainless.yaml @@ -97,11 +97,13 @@ resources: # [reference]: https://www.stainless.com/docs/reference/config#model models: image: "#/components/schemas/Image" + tag_image_request: "#/components/schemas/TagImageRequest" methods: list: get /images create: post /images get: get /images/{name} delete: delete /images/{name} + tag: post /images/{name}/tag instances: models: