Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions pkg/api/handlers/libpod/quadlets.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/containers/podman/v6/pkg/domain/entities"
"github.com/containers/podman/v6/pkg/domain/infra/abi"
"github.com/containers/podman/v6/pkg/util"
"github.com/sirupsen/logrus"
)

func ListQuadlets(w http.ResponseWriter, r *http.Request) {
Expand All @@ -35,3 +36,23 @@ func ListQuadlets(w http.ResponseWriter, r *http.Request) {

utils.WriteResponse(w, http.StatusOK, quadlets)
}

func GetQuadletPrint(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
name := utils.GetName(r)

containerEngine := abi.ContainerEngine{Libpod: runtime}

quadletContents, err := containerEngine.QuadletPrint(r.Context(), name)
if err != nil {
utils.Error(w, http.StatusNotFound, fmt.Errorf("no such quadlet: %s: %w", name, err))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a 404 for any error returned by print correct? 404 should only mean doesn't exist but looking at the code I see an error like is not a supported quadlet file type which doesn't sounds like a 404 to me but rather a 400 for example.

I guess it doesn't matter to match and handing it out specific status codes means we would need to use typed error in quadlet and then match them here with errors.Is/As to differentiate. Maybe that that will get to complicated for no practical gain.

return
}

w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte(quadletContents)); err != nil {
logrus.Errorf("Failed to write quadlet contents: %v", err)
return
}
}
7 changes: 7 additions & 0 deletions pkg/api/handlers/swagger/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ type podNotFound struct {
Body errorhandling.ErrorModel
}

// No such quadlet
// swagger:response
type quadletNotFound struct {
// in:body
Body errorhandling.ErrorModel
}

// No such manifest
// swagger:response
type manifestNotFound struct {
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/handlers/swagger/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,3 +527,10 @@ type quadletListResponse struct {
// in:body
Body []entities.ListQuadlet
}

// Quadlet file
// swagger:response
type quadletFileResponse struct {
// in:body
Body string
}
22 changes: 22 additions & 0 deletions pkg/api/server/register_quadlets.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,27 @@ func (s *APIServer) registerQuadletHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/internalError"
r.HandleFunc(VersionedPath("/libpod/quadlets/json"), s.APIHandler(libpod.ListQuadlets)).Methods(http.MethodGet)
// swagger:operation GET /libpod/quadlets/{name}/file libpod QuadletFileLibpod
// ---
// tags:
// - quadlets
// summary: Get quadlet file
// description: Get the contents of a Quadlet, displaying the file including all comments
// produces:
// - text/plain
// parameters:
// - in: path
// name: name
// type: string
// required: true
// description: the name of the quadlet with extension (e.g., "myapp.container")
// responses:
// 200:
// $ref: "#/responses/quadletFileResponse"
// 404:
// $ref: "#/responses/quadletNotFound"
// 500:
// $ref: "#/responses/internalError"
r.HandleFunc(VersionedPath("/libpod/quadlets/{name}/file"), s.APIHandler(libpod.GetQuadletPrint)).Methods(http.MethodGet)
return nil
}
56 changes: 53 additions & 3 deletions test/apiv2/36-quadlets.at
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,60 @@
# NOTE: Once podman-remote quadlet support is added we can enable the podman quadlet tests in
# test/system/253-podman-quadlet.bats which should cover it in more detail then.

## list volume
## Test list endpoint
t GET libpod/quadlets/json 200

# Example with filter applied (uncomment once needed)
# t GET libpod/quadlets/json?filters='{"name":["name.*"]}' 200
# Test 404 for non-existent quadlet
t GET libpod/quadlets/nonexistent.container 404

# Install a quadlet with a unique name
quadlet_name=quadlet-test-$(cat /proc/sys/kernel/random/uuid)

quadlet_container_name="$quadlet_name.container"
quadlet_build_name="$quadlet_name.build"

TMPDIR=$(mktemp -d podman-apiv2-test.quadlet.XXXXXXXX)

quadlet_container_file_content=$(cat << EOF
[Container]
Image=$IMAGE
EOF
)

quadlet_build_file_content=$(cat << EOF
[Build]
ImageTag=localhost/$quadlet_name
EOF
)

echo "$quadlet_container_file_content" > $TMPDIR/$quadlet_container_name
echo "$quadlet_build_file_content" > $TMPDIR/$quadlet_build_name

# this should ensure the .config/containers/systemd directory is created
podman quadlet install $TMPDIR/$quadlet_container_name
podman quadlet install $TMPDIR/$quadlet_build_name

filter_param=$(printf '{"name":["%s"]}' "$quadlet_name")
t GET "libpod/quadlets/json?filters=$filter_param" 200 \
length=2 \
.[0].Name="$quadlet_build_name" \
.[1].Name="$quadlet_container_name"

filter_param=$(printf '{"name":["%s"]}' "$quadlet_container_name")
t GET "libpod/quadlets/json?filters=$filter_param" 200 \
length=1 \
.[0].Name="$quadlet_container_name"

t GET "libpod/quadlets/$quadlet_name/file" 404

t GET "libpod/quadlets/$quadlet_container_name/file" 200
is "$output" "$quadlet_container_file_content"

t GET "libpod/quadlets/$quadlet_build_name/file" 200
is "$output" "$quadlet_build_file_content"

podman quadlet rm $quadlet_container_name
podman quadlet rm $quadlet_build_name
rm -rf $TMPDIR

# vim: filetype=sh