mirror of
https://github.com/containers/podman.git
synced 2026-02-05 15:45:08 +01:00
Merge pull request #27936 from inknos/get-exists-quadlet-api
Add GET /quadlets/{name}/exists
This commit is contained in:
@@ -29,6 +29,9 @@ var (
|
||||
// does not exist.
|
||||
ErrNoSuchExitCode = errors.New("no such exit code")
|
||||
|
||||
// ErrNoSuchQuadlet indicates the requested quadlet does not exist
|
||||
ErrNoSuchQuadlet = errors.New("no such quadlet")
|
||||
|
||||
// ErrDepExists indicates that the current object has dependencies and
|
||||
// cannot be removed before them.
|
||||
ErrDepExists = errors.New("dependency exists")
|
||||
|
||||
@@ -67,6 +67,25 @@ func GetQuadletPrint(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// QuadletExists checks if a quadlet exists by name
|
||||
func QuadletExists(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||
name := utils.GetName(r)
|
||||
|
||||
containerEngine := abi.ContainerEngine{Libpod: runtime}
|
||||
|
||||
report, err := containerEngine.QuadletExists(r.Context(), name)
|
||||
if err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
if !report.Value {
|
||||
utils.Error(w, http.StatusNotFound, fmt.Errorf("no such quadlet: %s", name))
|
||||
return
|
||||
}
|
||||
utils.WriteResponse(w, http.StatusNoContent, "")
|
||||
}
|
||||
|
||||
// extractQuadletFiles extracts quadlet files from tar archive to a temporary directory
|
||||
func extractQuadletFiles(tempDir string, r io.ReadCloser) ([]string, error) {
|
||||
quadletDir := filepath.Join(tempDir, "quadlets")
|
||||
|
||||
@@ -54,6 +54,28 @@ func (s *APIServer) registerQuadletHandlers(r *mux.Router) error {
|
||||
// 500:
|
||||
// $ref: "#/responses/internalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/quadlets/{name}/file"), s.APIHandler(libpod.GetQuadletPrint)).Methods(http.MethodGet)
|
||||
// swagger:operation GET /libpod/quadlets/{name}/exists libpod QuadletExistsLibpod
|
||||
// ---
|
||||
// tags:
|
||||
// - quadlets
|
||||
// summary: Check if quadlet exists
|
||||
// description: Check if a quadlet exists by name
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name
|
||||
// type: string
|
||||
// required: true
|
||||
// description: the name of the quadlet with extension (e.g., "myapp.container")
|
||||
// responses:
|
||||
// 204:
|
||||
// description: quadlet exists
|
||||
// 404:
|
||||
// $ref: "#/responses/quadletNotFound"
|
||||
// 500:
|
||||
// $ref: "#/responses/internalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/quadlets/{name}/exists"), s.APIHandler(libpod.QuadletExists)).Methods(http.MethodGet)
|
||||
// swagger:operation POST /libpod/quadlets libpod QuadletInstallLibpod
|
||||
// ---
|
||||
// tags:
|
||||
|
||||
@@ -94,6 +94,7 @@ type ContainerEngine interface { //nolint:interfacebloat
|
||||
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
|
||||
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
|
||||
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
|
||||
QuadletExists(ctx context.Context, name string) (*BoolReport, error)
|
||||
QuadletInstall(ctx context.Context, pathsOrURLs []string, options QuadletInstallOptions) (*QuadletInstallReport, error)
|
||||
QuadletList(ctx context.Context, options QuadletListOptions) ([]*ListQuadlet, error)
|
||||
QuadletPrint(ctx context.Context, quadlet string) (string, error)
|
||||
|
||||
@@ -719,6 +719,15 @@ func (ic *ContainerEngine) QuadletList(ctx context.Context, options entities.Qua
|
||||
return finalReports, nil
|
||||
}
|
||||
|
||||
// QuadletExists checks whether a quadlet with the given name exists.
|
||||
func (ic *ContainerEngine) QuadletExists(_ context.Context, name string) (*entities.BoolReport, error) {
|
||||
_, err := getQuadletPathByName(name)
|
||||
if err != nil && !errors.Is(err, define.ErrNoSuchQuadlet) {
|
||||
return nil, err
|
||||
}
|
||||
return &entities.BoolReport{Value: err == nil}, nil
|
||||
}
|
||||
|
||||
// Retrieve path to a Quadlet file given full name including extension
|
||||
func getQuadletPathByName(name string) (string, error) {
|
||||
// Check if we were given a valid extension
|
||||
@@ -737,7 +746,7 @@ func getQuadletPathByName(name string) (string, error) {
|
||||
}
|
||||
return testPath, nil
|
||||
}
|
||||
return "", fmt.Errorf("could not locate quadlet %q in any supported quadlet directory", name)
|
||||
return "", fmt.Errorf("could not locate quadlet %q in any supported quadlet directory: %w", name, define.ErrNoSuchQuadlet)
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) QuadletPrint(_ context.Context, quadlet string) (string, error) {
|
||||
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
|
||||
var errNotImplemented = errors.New("not implemented for the remote Podman client")
|
||||
|
||||
func (ic *ContainerEngine) QuadletExists(_ context.Context, _ string) (*entities.BoolReport, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) QuadletInstall(_ context.Context, _ []string, _ entities.QuadletInstallOptions) (*entities.QuadletInstallReport, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ t GET libpod/quadlets/nonexistent.container 405
|
||||
# Test 404 for non-existent quadlet
|
||||
t GET libpod/quadlets/nonexistent.container/file 404
|
||||
|
||||
# Test 404 for non-existent quadlet exists endpoint
|
||||
t GET libpod/quadlets/nonexistent.container/exists 404
|
||||
|
||||
# Test 500 for invalid quadlet extension (not a user-facing "not found" but an input error)
|
||||
t GET libpod/quadlets/invalid.badext/exists 500
|
||||
|
||||
# Install a quadlet with a unique name
|
||||
quadlet_name=quadlet-test-$(cat /proc/sys/kernel/random/uuid)
|
||||
|
||||
@@ -80,6 +86,12 @@ is "$output" "$quadlet_container_file_content"
|
||||
t GET "libpod/quadlets/$quadlet_build_name/file" 200
|
||||
is "$output" "$quadlet_build_file_content"
|
||||
|
||||
# Test exists endpoint returns 204 for existing quadlet
|
||||
t GET "libpod/quadlets/$quadlet_container_name/exists" 204
|
||||
|
||||
# Test exists endpoint returns 500 for quadlet without extension
|
||||
t GET "libpod/quadlets/$quadlet_name/exists" 500
|
||||
|
||||
podman quadlet rm $quadlet_container_name
|
||||
podman quadlet rm $quadlet_build_name
|
||||
rm -rf $TMPD
|
||||
@@ -319,16 +331,20 @@ EOF
|
||||
echo "$quadlet_1_content" > "$TMPDIR/$quadlet_1"
|
||||
podman quadlet install $TMPDIR/$quadlet_1
|
||||
|
||||
t GET "libpod/quadlets/$quadlet_1/exists" 204
|
||||
|
||||
t DELETE "libpod/quadlets/$quadlet_1" 200 \
|
||||
'.Removed|length=1' \
|
||||
'.QuadletErrors|length=0'
|
||||
|
||||
t GET "libpod/quadlets/$quadlet_1/file" 404
|
||||
|
||||
t DELETE "libpod/quadlets/nonexistent.container" 200 \
|
||||
# Verify exists returns 404 after deletion
|
||||
t GET "libpod/quadlets/$quadlet_1/exists" 404
|
||||
|
||||
t DELETE "libpod/quadlets/nonexistent.container" 404 \
|
||||
'.Removed|length=0' \
|
||||
'.Errors|length=1' \
|
||||
'.Errors~.*could not locate quadlet.*'
|
||||
.cause='no such quadlet'
|
||||
|
||||
t DELETE "libpod/quadlets/nonexistent.container?ignore=true" 200 \
|
||||
'.Removed|length=1' \
|
||||
@@ -353,7 +369,7 @@ podman quadlet install $TMPDIR/$quadlet_2
|
||||
if systemctl --user start "${quadlet_2%.*}.service" > /dev/null 2>&1; then
|
||||
if systemctl --user is-active --quiet ${quadlet_2%.*}.service; then
|
||||
t DELETE "libpod/quadlets/$quadlet_2" 400 \
|
||||
.cause~.*'container'.*'is running and force is not set, refusing to remove'.*
|
||||
.cause~.*'quadlet is running'
|
||||
|
||||
t DELETE "libpod/quadlets/$quadlet_2?force=true" 200
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user