diff options
Diffstat (limited to 'pkg/api/handlers')
-rw-r--r-- | pkg/api/handlers/compat/images.go | 32 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_push.go | 1 | ||||
-rw-r--r-- | pkg/api/handlers/compat/networks.go | 54 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/containers_create.go | 3 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 71 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/images_push.go | 144 |
6 files changed, 195 insertions, 110 deletions
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 2f8d151d8..39bd165d6 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -23,6 +23,7 @@ import ( "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/infra/abi" "github.com/containers/storage" + "github.com/docker/docker/pkg/jsonmessage" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" @@ -325,16 +326,8 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { loop: // break out of for/select infinite loop for { - var report struct { - Stream string `json:"stream,omitempty"` - Status string `json:"status,omitempty"` - Progress struct { - Current uint64 `json:"current,omitempty"` - Total int64 `json:"total,omitempty"` - } `json:"progressDetail,omitempty"` - Error string `json:"error,omitempty"` - Id string `json:"id,omitempty"` //nolint:revive,stylecheck - } + report := jsonmessage.JSONMessage{} + report.Progress = &jsonmessage.JSONProgress{} select { case e := <-progress: switch e.Event { @@ -342,14 +335,15 @@ loop: // break out of for/select infinite loop report.Status = "Pulling fs layer" case types.ProgressEventRead: report.Status = "Downloading" - report.Progress.Current = e.Offset + report.Progress.Current = int64(e.Offset) report.Progress.Total = e.Artifact.Size + report.ProgressMessage = report.Progress.String() case types.ProgressEventSkipped: report.Status = "Already exists" case types.ProgressEventDone: report.Status = "Download complete" } - report.Id = e.Artifact.Digest.Encoded()[0:12] + report.ID = e.Artifact.Digest.Encoded()[0:12] if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } @@ -358,7 +352,11 @@ loop: // break out of for/select infinite loop err := pullRes.err pulledImages := pullRes.images if err != nil { - report.Error = err.Error() + msg := err.Error() + report.Error = &jsonmessage.JSONError{ + Message: msg, + } + report.ErrorMessage = msg } else { if len(pulledImages) > 0 { img := pulledImages[0].ID() @@ -367,9 +365,13 @@ loop: // break out of for/select infinite loop } else { report.Status = "Download complete" } - report.Id = img[0:12] + report.ID = img[0:12] } else { - report.Error = "internal error: no images pulled" + msg := "internal error: no images pulled" + report.Error = &jsonmessage.JSONError{ + Message: msg, + } + report.ErrorMessage = msg } } if err := enc.Encode(report); err != nil { diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index bb82ef10d..f29808124 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -156,6 +156,7 @@ loop: // break out of for/select infinite loop Current: int64(e.Offset), Total: e.Artifact.Size, } + report.ProgressMessage = report.Progress.String() case types.ProgressEventSkipped: report.Status = "Layer already exists" case types.ProgressEventDone: diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index 65177218a..29d1398cf 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -23,6 +23,13 @@ import ( "github.com/sirupsen/logrus" ) +func normalizeNetworkName(rt *libpod.Runtime, name string) (string, bool) { + if name == nettypes.BridgeNetworkDriver { + return rt.Network().DefaultNetworkName(), true + } + return name, false +} + func InspectNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -44,13 +51,13 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusBadRequest, define.ErrInvalidArg) return } - name := utils.GetName(r) + name, changed := normalizeNetworkName(runtime, utils.GetName(r)) net, err := runtime.Network().NetworkInspect(name) if err != nil { utils.NetworkNotFound(w, name, err) return } - report, err := convertLibpodNetworktoDockerNetwork(runtime, net) + report, err := convertLibpodNetworktoDockerNetwork(runtime, &net, changed) if err != nil { utils.InternalServerError(w, err) return @@ -58,7 +65,7 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, report) } -func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, network nettypes.Network) (*types.NetworkResource, error) { +func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, network *nettypes.Network, changeDefaultName bool) (*types.NetworkResource, error) { cons, err := runtime.GetAllContainers() if err != nil { return nil, err @@ -107,11 +114,15 @@ func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, network nettyp Config: ipamConfigs, } + name := network.Name + if changeDefaultName && name == runtime.Network().DefaultNetworkName() { + name = nettypes.BridgeNetworkDriver + } report := types.NetworkResource{ - Name: network.Name, - ID: network.ID, - Driver: network.Driver, - // TODO add Created: , + Name: name, + ID: network.ID, + Driver: network.Driver, + Created: network.Created, Internal: network.Internal, EnableIPv6: network.IPv6Enabled, Labels: network.Labels, @@ -149,7 +160,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { } reports := make([]*types.NetworkResource, 0, len(nets)) for _, net := range nets { - report, err := convertLibpodNetworktoDockerNetwork(runtime, net) + report, err := convertLibpodNetworktoDockerNetwork(runtime, &net, true) if err != nil { utils.InternalServerError(w, err) return @@ -182,27 +193,22 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { network.Options = make(map[string]string) - // TODO: we should consider making this constants in c/common/libnetwork/types + // dockers bridge networks are always isolated from each other + if network.Driver == nettypes.BridgeNetworkDriver { + network.Options[nettypes.IsolateOption] = "true" + } + for opt, optVal := range networkCreate.Options { switch opt { - case "mtu": + case nettypes.MTUOption: fallthrough case "com.docker.network.driver.mtu": - if network.Driver == nettypes.BridgeNetworkDriver { - network.Options["mtu"] = optVal - } - case "icc": - fallthrough - case "com.docker.network.bridge.enable_icc": - // TODO: needs to be implemented - if network.Driver == nettypes.BridgeNetworkDriver { - responseWarning = "com.docker.network.bridge.enable_icc is not currently implemented" - } + network.Options[nettypes.MTUOption] = optVal case "com.docker.network.bridge.name": if network.Driver == nettypes.BridgeNetworkDriver { network.NetworkInterface = optVal } - case "mode": + case nettypes.ModeOption: if network.Driver == nettypes.MacVLANNetworkDriver || network.Driver == nettypes.IPVLANNetworkDriver { network.Options[opt] = optVal } @@ -305,7 +311,7 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) { Timeout: query.Timeout, } - name := utils.GetName(r) + name, _ := normalizeNetworkName(runtime, utils.GetName(r)) reports, err := ic.NetworkRm(r.Context(), []string{name}, options) if err != nil { utils.Error(w, http.StatusInternalServerError, err) @@ -340,7 +346,7 @@ func Connect(w http.ResponseWriter, r *http.Request) { netOpts := nettypes.PerNetworkOptions{} - name := utils.GetName(r) + name, _ := normalizeNetworkName(runtime, utils.GetName(r)) if netConnect.EndpointConfig != nil { if netConnect.EndpointConfig.Aliases != nil { netOpts.Aliases = netConnect.EndpointConfig.Aliases @@ -416,7 +422,7 @@ func Disconnect(w http.ResponseWriter, r *http.Request) { return } - name := utils.GetName(r) + name, _ := normalizeNetworkName(runtime, utils.GetName(r)) err := runtime.DisconnectContainerFromNetwork(netDisconnect.Container, name, netDisconnect.Force) if err != nil { if errors.Is(err, define.ErrNoSuchCtr) { diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go index e4964d602..1307c267a 100644 --- a/pkg/api/handlers/libpod/containers_create.go +++ b/pkg/api/handlers/libpod/containers_create.go @@ -31,6 +31,9 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { ContainerNetworkConfig: specgen.ContainerNetworkConfig{ UseImageHosts: conf.Containers.NoHosts, }, + ContainerSecurityConfig: specgen.ContainerSecurityConfig{ + Umask: conf.Containers.Umask, + }, } if err := json.NewDecoder(r.Body).Decode(&sg); err != nil { diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index ed1c65f8e..67943ecf1 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -1,7 +1,6 @@ package libpod import ( - "context" "errors" "fmt" "io" @@ -14,13 +13,11 @@ import ( "github.com/containers/buildah" "github.com/containers/common/libimage" "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/types" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers" "github.com/containers/podman/v4/pkg/api/handlers/utils" api "github.com/containers/podman/v4/pkg/api/types" - "github.com/containers/podman/v4/pkg/auth" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/domain/entities/reports" "github.com/containers/podman/v4/pkg/domain/infra/abi" @@ -416,74 +413,6 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, report) } -// PushImage is the handler for the compat http endpoint for pushing images. -func PushImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) - runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - - query := struct { - All bool `schema:"all"` - Destination string `schema:"destination"` - Format string `schema:"format"` - RemoveSignatures bool `schema:"removeSignatures"` - TLSVerify bool `schema:"tlsVerify"` - }{ - // This is where you can override the golang default value for one of fields - } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) - return - } - - source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path - if _, err := utils.ParseStorageReference(source); err != nil { - utils.Error(w, http.StatusBadRequest, err) - return - } - - destination := query.Destination - if destination == "" { - destination = source - } - - if err := utils.IsRegistryReference(destination); err != nil { - utils.Error(w, http.StatusBadRequest, err) - return - } - - authconf, authfile, err := auth.GetCredentials(r) - if err != nil { - utils.Error(w, http.StatusBadRequest, err) - return - } - defer auth.RemoveAuthfile(authfile) - var username, password string - if authconf != nil { - username = authconf.Username - password = authconf.Password - } - options := entities.ImagePushOptions{ - All: query.All, - Authfile: authfile, - Format: query.Format, - Password: password, - Quiet: true, - RemoveSignatures: query.RemoveSignatures, - Username: username, - } - if _, found := r.URL.Query()["tlsVerify"]; found { - options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) - } - - imageEngine := abi.ImageEngine{Libpod: runtime} - if err := imageEngine.Push(context.Background(), source, destination, options); err != nil { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) - return - } - - utils.WriteResponse(w, http.StatusOK, "") -} - func CommitContainer(w http.ResponseWriter, r *http.Request) { var ( destImage string diff --git a/pkg/api/handlers/libpod/images_push.go b/pkg/api/handlers/libpod/images_push.go new file mode 100644 index 000000000..f427dc01b --- /dev/null +++ b/pkg/api/handlers/libpod/images_push.go @@ -0,0 +1,144 @@ +package libpod + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/containers/image/v5/types" + "github.com/containers/podman/v4/libpod" + "github.com/containers/podman/v4/pkg/api/handlers/utils" + api "github.com/containers/podman/v4/pkg/api/types" + "github.com/containers/podman/v4/pkg/auth" + "github.com/containers/podman/v4/pkg/channel" + "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/containers/podman/v4/pkg/domain/infra/abi" + "github.com/gorilla/schema" + "github.com/sirupsen/logrus" +) + +// PushImage is the handler for the compat http endpoint for pushing images. +func PushImage(w http.ResponseWriter, r *http.Request) { + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + + query := struct { + All bool `schema:"all"` + Destination string `schema:"destination"` + Format string `schema:"format"` + RemoveSignatures bool `schema:"removeSignatures"` + TLSVerify bool `schema:"tlsVerify"` + Quiet bool `schema:"quiet"` + }{ + // #14971: older versions did not sent *any* data, so we need + // to be quiet by default to remain backwards compatible + Quiet: true, + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err)) + return + } + + source := strings.TrimSuffix(utils.GetName(r), "/push") // GetName returns the entire path + if _, err := utils.ParseStorageReference(source); err != nil { + utils.Error(w, http.StatusBadRequest, err) + return + } + + destination := query.Destination + if destination == "" { + destination = source + } + + if err := utils.IsRegistryReference(destination); err != nil { + utils.Error(w, http.StatusBadRequest, err) + return + } + + authconf, authfile, err := auth.GetCredentials(r) + if err != nil { + utils.Error(w, http.StatusBadRequest, err) + return + } + defer auth.RemoveAuthfile(authfile) + + var username, password string + if authconf != nil { + username = authconf.Username + password = authconf.Password + } + options := entities.ImagePushOptions{ + All: query.All, + Authfile: authfile, + Format: query.Format, + Password: password, + Quiet: true, + RemoveSignatures: query.RemoveSignatures, + Username: username, + } + + if _, found := r.URL.Query()["tlsVerify"]; found { + options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + imageEngine := abi.ImageEngine{Libpod: runtime} + + // Let's keep thing simple when running in quiet mode and push directly. + if query.Quiet { + if err := imageEngine.Push(context.Background(), source, destination, options); err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("error pushing image %q: %w", destination, err)) + return + } + utils.WriteResponse(w, http.StatusOK, "") + return + } + + writer := channel.NewWriter(make(chan []byte)) + defer writer.Close() + options.Writer = writer + + pushCtx, pushCancel := context.WithCancel(r.Context()) + var pushError error + go func() { + defer pushCancel() + pushError = imageEngine.Push(pushCtx, source, destination, options) + }() + + flush := func() { + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + } + + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + flush() + + enc := json.NewEncoder(w) + enc.SetEscapeHTML(true) + for { + var report entities.ImagePushReport + select { + case s := <-writer.Chan(): + report.Stream = string(s) + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) + } + flush() + case <-pushCtx.Done(): + if pushError != nil { + report.Error = pushError.Error() + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) + } + } + flush() + return + case <-r.Context().Done(): + // Client has closed connection + return + } + } +} |