fix(swarm): fix the Host field when listing images (#369)

Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com>
release/2.27.0-rc3
LP B 2025-02-12 00:47:50 +01:00 committed by GitHub
parent 3c6f61134e
commit 9e04145875
2 changed files with 23 additions and 19 deletions

View File

@ -3,8 +3,8 @@ package client
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"maps"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -141,7 +141,6 @@ func createAgentClient(endpoint *portainer.Endpoint, endpointURL string, signatu
type NodeNameTransport struct { type NodeNameTransport struct {
*http.Transport *http.Transport
nodeNames map[string]string
} }
func (t *NodeNameTransport) RoundTrip(req *http.Request) (*http.Response, error) { func (t *NodeNameTransport) RoundTrip(req *http.Request) (*http.Response, error) {
@ -176,18 +175,19 @@ func (t *NodeNameTransport) RoundTrip(req *http.Request) (*http.Response, error)
return resp, nil return resp, nil
} }
t.nodeNames = make(map[string]string) nodeNames, ok := req.Context().Value("nodeNames").(map[string]string)
for _, r := range rs { if ok {
t.nodeNames[r.ID] = r.Portainer.Agent.NodeName for idx, r := range rs {
// as there is no way to differentiate the same image available in multiple nodes only by their ID
// we append the index of the image in the payload response to match the node name later
// from the image.Summary[] list returned by docker's client.ImageList()
nodeNames[fmt.Sprintf("%s-%d", r.ID, idx)] = r.Portainer.Agent.NodeName
}
} }
return resp, err return resp, err
} }
func (t *NodeNameTransport) NodeNames() map[string]string {
return maps.Clone(t.nodeNames)
}
func httpClient(endpoint *portainer.Endpoint, timeout *time.Duration) (*http.Client, error) { func httpClient(endpoint *portainer.Endpoint, timeout *time.Duration) (*http.Client, error) {
transport := &NodeNameTransport{ transport := &NodeNameTransport{
Transport: &http.Transport{}, Transport: &http.Transport{},

View File

@ -1,10 +1,11 @@
package images package images
import ( import (
"context"
"fmt"
"net/http" "net/http"
"strings" "strings"
"github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/http/handler/docker/utils" "github.com/portainer/portainer/api/http/handler/docker/utils"
"github.com/portainer/portainer/api/set" "github.com/portainer/portainer/api/set"
httperror "github.com/portainer/portainer/pkg/libhttp/error" httperror "github.com/portainer/portainer/pkg/libhttp/error"
@ -46,17 +47,16 @@ func (handler *Handler) imagesList(w http.ResponseWriter, r *http.Request) *http
return httpErr return httpErr
} }
images, err := cli.ImageList(r.Context(), image.ListOptions{}) nodeNames := make(map[string]string)
// Pass the node names map to the context so the custom NodeNameTransport can use it
ctx := context.WithValue(r.Context(), "nodeNames", nodeNames)
images, err := cli.ImageList(ctx, image.ListOptions{})
if err != nil { if err != nil {
return httperror.InternalServerError("Unable to retrieve Docker images", err) return httperror.InternalServerError("Unable to retrieve Docker images", err)
} }
// Extract the node name from the custom transport
nodeNames := make(map[string]string)
if t, ok := cli.HTTPClient().Transport.(*client.NodeNameTransport); ok {
nodeNames = t.NodeNames()
}
withUsage, err := request.RetrieveBooleanQueryParameter(r, "withUsage", true) withUsage, err := request.RetrieveBooleanQueryParameter(r, "withUsage", true)
if err != nil { if err != nil {
return httperror.BadRequest("Invalid query parameter: withUsage", err) return httperror.BadRequest("Invalid query parameter: withUsage", err)
@ -86,7 +86,11 @@ func (handler *Handler) imagesList(w http.ResponseWriter, r *http.Request) *http
imagesList[i] = ImageResponse{ imagesList[i] = ImageResponse{
Created: image.Created, Created: image.Created,
NodeName: nodeNames[image.ID], // Only works if the order of `images` is not changed between unmarshaling the agent's response
// in NodeNameTransport.RoundTrip() (api/docker/client/client.go)
// and docker's cli.ImageList()
// As both functions unmarshal the same response body, the resulting array will be ordered the same way.
NodeName: nodeNames[fmt.Sprintf("%s-%d", image.ID, i)],
ID: image.ID, ID: image.ID,
Size: image.Size, Size: image.Size,
Tags: image.RepoTags, Tags: image.RepoTags,