diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/compat/containers.go | 7 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images.go | 12 | ||||
-rw-r--r-- | pkg/api/handlers/compat/images_build.go | 6 | ||||
-rw-r--r-- | pkg/api/handlers/utils/containers.go | 35 | ||||
-rw-r--r-- | pkg/bindings/images/build.go | 2 | ||||
-rw-r--r-- | pkg/specgen/generate/kube/kube.go | 4 | ||||
-rw-r--r-- | pkg/systemd/generate/common.go | 23 | ||||
-rw-r--r-- | pkg/systemd/generate/containers.go | 31 | ||||
-rw-r--r-- | pkg/systemd/generate/containers_test.go | 46 |
9 files changed, 142 insertions, 24 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 263d64a7b..6bc02dd2b 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -379,6 +379,11 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, if err != nil { return nil, err } + // Docker uses UTC + if inspect != nil && inspect.State != nil { + inspect.State.StartedAt = inspect.State.StartedAt.UTC() + inspect.State.FinishedAt = inspect.State.FinishedAt.UTC() + } i, err := json.Marshal(inspect.State) if err != nil { return nil, err @@ -425,7 +430,7 @@ func LibpodToContainerJSON(l *libpod.Container, sz bool) (*types.ContainerJSON, cb := types.ContainerJSONBase{ ID: l.ID(), - Created: l.CreatedTime().Format(time.RFC3339Nano), + Created: l.CreatedTime().UTC().Format(time.RFC3339Nano), // Docker uses UTC Path: inspect.Path, Args: inspect.Args, State: &state, diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 4b7a2a71c..3f4320efa 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -324,7 +324,11 @@ loop: // break out of for/select infinite loop flush() case <-runCtx.Done(): if !failed { - report.Status = "Pull complete" + if utils.IsLibpodRequest(r) { + report.Status = "Pull complete" + } else { + report.Status = "Download complete" + } report.Id = img[0:12] if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) @@ -455,10 +459,6 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, fmt.Errorf("no images to download")) return } - if len(query.Names) > 1 { - utils.Error(w, "Something went wrong.", http.StatusNotImplemented, fmt.Errorf("getting multiple image is not supported yet")) - return - } images := query.Names tmpfile, err := ioutil.TempFile("", "api.tar") @@ -474,7 +474,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { imageEngine := abi.ImageEngine{Libpod: runtime} - saveOptions := entities.ImageSaveOptions{Format: "docker-archive", Output: tmpfile.Name()} + saveOptions := entities.ImageSaveOptions{Format: "docker-archive", Output: tmpfile.Name(), MultiImageArchive: true} if err := imageEngine.Save(r.Context(), images[0], images[1:], saveOptions); err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index ec40fdd2d..6ff557291 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -24,7 +24,7 @@ import ( "github.com/containers/podman/v3/pkg/channel" "github.com/containers/storage/pkg/archive" "github.com/gorilla/schema" - specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -553,6 +553,10 @@ loop: } } break loop + case <-r.Context().Done(): + cancel() + logrus.Infof("Client disconnect reported for build %q / %q.", registry, query.Dockerfile) + return } } } diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index c4c9cc2ea..6c708f74e 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -175,7 +176,7 @@ func waitDockerCondition(ctx context.Context, containerName string, interval tim var code int32 switch dockerCondition { case "next-exit": - code, err = waitNextExit(containerWait) + code, err = waitNextExit(ctx, containerName) case "removed": code, err = waitRemoved(containerWait) case "not-running", "": @@ -202,12 +203,32 @@ func waitRemoved(ctrWait containerWaitFn) (int32, error) { return code, err } -func waitNextExit(ctrWait containerWaitFn) (int32, error) { - _, err := ctrWait(define.ContainerStateRunning) - if err != nil { - return -1, err - } - return ctrWait(notRunningStates...) +func waitNextExit(ctx context.Context, containerName string) (int32, error) { + runtime := ctx.Value("runtime").(*libpod.Runtime) + containerEngine := &abi.ContainerEngine{Libpod: runtime} + eventChannel := make(chan *events.Event) + errChannel := make(chan error) + opts := entities.EventsOptions{ + EventChan: eventChannel, + Filter: []string{"event=died", fmt.Sprintf("container=%s", containerName)}, + Stream: true, + } + + // ctx is used to cancel event watching goroutine + ctx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { + errChannel <- containerEngine.Events(ctx, opts) + }() + + evt, ok := <-eventChannel + if ok { + return int32(evt.ContainerExitCode), nil + } + // if ok == false then containerEngine.Events() has exited + // it may happen if request was canceled (e.g. client closed connection prematurely) or + // the server is in process of shutting down + return -1, <-errChannel } func waitNotRunning(ctrWait containerWaitFn) (int32, error) { diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 6acfcc1c8..f5e7c0c98 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -127,6 +127,8 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO } if options.RemoveIntermediateCtrs { params.Set("rm", "1") + } else { + params.Set("rm", "0") } if len(options.From) > 0 { params.Set("from", options.From) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index ccce3edba..4e41061a5 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -23,6 +23,10 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) p := specgen.NewPodSpecGenerator() p.Name = podName p.Labels = podYAML.ObjectMeta.Labels + // Kube pods must share {ipc, net, uts} by default + p.SharedNamespaces = append(p.SharedNamespaces, "ipc") + p.SharedNamespaces = append(p.SharedNamespaces, "net") + p.SharedNamespaces = append(p.SharedNamespaces, "uts") // TODO we only configure Process namespace. We also need to account for Host{IPC,Network,PID} // which is not currently possible with pod create if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index eafd45528..0f667e2f4 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -89,19 +89,24 @@ func filterCommonContainerFlags(command []string, argCount int) []string { // see: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines func escapeSystemdArguments(command []string) []string { for i := range command { - command[i] = strings.ReplaceAll(command[i], "$", "$$") - command[i] = strings.ReplaceAll(command[i], "%", "%%") - if strings.ContainsAny(command[i], " \t") { - command[i] = strconv.Quote(command[i]) - } else if strings.Contains(command[i], `\`) { - // strconv.Quote also escapes backslashes so - // we should replace only if strconv.Quote was not used - command[i] = strings.ReplaceAll(command[i], `\`, `\\`) - } + command[i] = escapeSystemdArg(command[i]) } return command } +func escapeSystemdArg(arg string) string { + arg = strings.ReplaceAll(arg, "$", "$$") + arg = strings.ReplaceAll(arg, "%", "%%") + if strings.ContainsAny(arg, " \t") { + arg = strconv.Quote(arg) + } else if strings.Contains(arg, `\`) { + // strconv.Quote also escapes backslashes so + // we should replace only if strconv.Quote was not used + arg = strings.ReplaceAll(arg, `\`, `\\`) + } + return arg +} + func removeDetachArg(args []string, argCount int) []string { // "--detach=false" could also be in the container entrypoint // split them off so we do not remove it there diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index e06655a8d..eb1fb67ff 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -54,6 +54,12 @@ type containerInfo struct { // CreateCommand is the full command plus arguments of the process the // container has been created with. CreateCommand []string + // containerEnv stores the container environment variables + containerEnv []string + // ExtraEnvs contains the container environment variables referenced + // by only the key in the container create command, e.g. --env FOO. + // This is only used with --new + ExtraEnvs []string // EnvVariable is generate.EnvVariable and must not be set. EnvVariable string // ExecStartPre of the unit. @@ -87,6 +93,9 @@ After={{{{- range $index, $value := .BoundToServices -}}}}{{{{if $index}}}} {{{{ [Service] Environment={{{{.EnvVariable}}}}=%n +{{{{- if .ExtraEnvs}}}} +Environment={{{{- range $index, $value := .ExtraEnvs -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}} +{{{{- end}}}} Restart={{{{.RestartPolicy}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}} {{{{- if .ExecStartPre}}}} @@ -153,6 +162,8 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste return nil, errors.Errorf("could not lookup container's runroot: got empty string") } + envs := config.Spec.Process.Env + info := containerInfo{ ServiceName: serviceName, ContainerNameOrID: nameOrID, @@ -163,6 +174,7 @@ func generateContainerInfo(ctr *libpod.Container, options entities.GenerateSyste CreateCommand: createCommand, GraphRoot: graphRoot, RunRoot: runRoot, + containerEnv: envs, } return &info, nil @@ -248,6 +260,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst fs.BoolP("detach", "d", false, "") fs.String("name", "", "") fs.Bool("replace", false, "") + fs.StringArrayP("env", "e", nil, "") fs.Parse(remainingCmd) remainingCmd = filterCommonContainerFlags(remainingCmd, fs.NArg()) @@ -304,6 +317,24 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst remainingCmd = removeReplaceArg(remainingCmd, fs.NArg()) } } + + envs, err := fs.GetStringArray("env") + if err != nil { + return "", err + } + for _, env := range envs { + // if env arg does not contain a equal sign we have to add the envar to the unit + // because it does try to red the value from the environment + if !strings.Contains(env, "=") { + for _, containerEnv := range info.containerEnv { + split := strings.SplitN(containerEnv, "=", 2) + if split[0] == env { + info.ExtraEnvs = append(info.ExtraEnvs, escapeSystemdArg(containerEnv)) + } + } + } + } + startCommand = append(startCommand, remainingCmd...) startCommand = escapeSystemdArguments(startCommand) diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 899ba6bfa..75b08526b 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -445,6 +445,32 @@ Type=forking [Install] WantedBy=multi-user.target default.target ` + + goodNewWithEnvar := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +RequiresMountsFor=/var/lib/containers/storage /var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Environment=FOO=abc "BAR=my test" USER=%%a +Restart=always +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --env FOO --env=BAR --env=MYENV=2 -e USER awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` tests := []struct { name string info containerInfo @@ -873,6 +899,26 @@ WantedBy=multi-user.target default.target false, false, }, + {"good with environment variables", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + CreateCommand: []string{"I'll get stripped", "create", "--env", "FOO", "--env=BAR", "--env=MYENV=2", "-e", "USER", "awesome-image:latest"}, + containerEnv: []string{"FOO=abc", "BAR=my test", "USER=%a", "MYENV=2"}, + EnvVariable: define.EnvVariable, + }, + goodNewWithEnvar, + true, + false, + false, + }, } for _, tt := range tests { test := tt |