diff options
Diffstat (limited to 'pkg')
43 files changed, 956 insertions, 517 deletions
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index fdd9f6ab3..8b21d6b94 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -375,7 +375,7 @@ func (r *LocalRuntime) selectDetachKeys(flagValue string) (string, error) { config, err := r.GetConfig() if err != nil { - return "", errors.Wrapf(err, "unable to retrive runtime config") + return "", errors.Wrapf(err, "unable to retrieve runtime config") } if config.DetachKeys != "" { return config.DetachKeys, nil @@ -609,11 +609,12 @@ func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) return state == define.ContainerStateExited }) - if c.Import != "" { + switch { + case c.Import != "": containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name) - } else if c.All { + case c.All: containers, err = r.GetContainers(filterFuncs...) - } else { + default: containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime) } if err != nil { @@ -835,25 +836,26 @@ func (r *LocalRuntime) Restart(ctx context.Context, c *cliconfig.RestartValues) inputTimeout := c.Timeout // Handle --latest - if c.Latest { + switch { + case c.Latest: lastCtr, err := r.Runtime.GetLatestContainer() if err != nil { return nil, nil, errors.Wrapf(err, "unable to get latest container") } restartContainers = append(restartContainers, lastCtr) - } else if c.Running { + case c.Running: containers, err = r.GetRunningContainers() if err != nil { return nil, nil, err } restartContainers = append(restartContainers, containers...) - } else if c.All { + case c.All: containers, err = r.Runtime.GetAllContainers() if err != nil { return nil, nil, err } restartContainers = append(restartContainers, containers...) - } else { + default: for _, id := range c.InputArgs { ctr, err := r.Runtime.LookupContainer(id) if err != nil { @@ -1048,7 +1050,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal } // Prune removes stopped containers -func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, filters []string) ([]string, map[string]error, error) { +func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, filters []string) ([]string, map[string]error, error) { var ( ok = []string{} failures = map[string]error{} @@ -1100,7 +1102,7 @@ func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, fi pool.Add(shared.Job{ ID: ctr.ID(), Fn: func() error { - err := r.Runtime.RemoveContainer(ctx, ctr, force, false) + err := r.Runtime.RemoveContainer(ctx, ctr, false, false) if err != nil { logrus.Debugf("Failed to prune container %s: %s", ctr.ID(), err.Error()) } diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index 36db4af68..60ee3cb2d 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -922,7 +922,7 @@ func (r *LocalRuntime) Top(cli *cliconfig.TopValues) ([]string, error) { } // Prune removes stopped containers -func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, filter []string) ([]string, map[string]error, error) { +func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, filter []string) ([]string, map[string]error, error) { var ( ok = []string{} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 5f880e807..40089797d 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -59,7 +59,7 @@ type Volume struct { // VolumeFilter is for filtering volumes on the client type VolumeFilter func(*Volume) bool -// GetRuntimeNoStore returns a localruntime struct wit an embedded runtime but +// GetRuntimeNoStore returns a localruntime struct with an embedded runtime but // without a configured storage. func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { runtime, err := libpodruntime.GetRuntimeNoStore(ctx, c) @@ -407,7 +407,8 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { } w := bufio.NewWriter(os.Stdout) for event := range eventChannel { - if c.Format == formats.JSONString { + switch { + case c.Format == formats.JSONString: jsonStr, err := event.ToJSONString() if err != nil { return errors.Wrapf(err, "unable to format json") @@ -415,11 +416,11 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { if _, err := w.Write([]byte(jsonStr)); err != nil { return err } - } else if len(c.Format) > 0 { + case len(c.Format) > 0: if err := tmpl.Execute(w, event); err != nil { return err } - } else { + default: if _, err := w.Write([]byte(event.ToHumanReadable())); err != nil { return err } diff --git a/pkg/adapter/shortcuts/shortcuts.go b/pkg/adapter/shortcuts/shortcuts.go index 4f6cfd6a3..8a8459c6c 100644 --- a/pkg/adapter/shortcuts/shortcuts.go +++ b/pkg/adapter/shortcuts/shortcuts.go @@ -42,12 +42,13 @@ func GetContainersByContext(all, latest bool, names []string, runtime *libpod.Ru var ctr *libpod.Container ctrs = []*libpod.Container{} - if all { + switch { + case all: ctrs, err = runtime.GetAllContainers() - } else if latest { + case latest: ctr, err = runtime.GetLatestContainer() ctrs = append(ctrs, ctr) - } else { + default: for _, n := range names { ctr, e := runtime.LookupContainer(n) if e != nil { diff --git a/pkg/api/Makefile b/pkg/api/Makefile new file mode 100644 index 000000000..5fb4e7da5 --- /dev/null +++ b/pkg/api/Makefile @@ -0,0 +1,3 @@ +swagger: + swagger generate spec -o swagger.yaml -w ./ + cat tags.yaml >> swagger.yaml diff --git a/pkg/api/handlers/generic/config.go b/pkg/api/handlers/generic/config.go new file mode 100644 index 000000000..f715d25eb --- /dev/null +++ b/pkg/api/handlers/generic/config.go @@ -0,0 +1,9 @@ +package generic + +// ContainerCreateResponse is the response struct for creating a container +type ContainerCreateResponse struct { + // ID of the container created + Id string `json:"Id"` + // Warnings during container creation + Warnings []string `json:"Warnings"` +} diff --git a/pkg/api/handlers/generic/containers_create.go b/pkg/api/handlers/generic/containers_create.go index ef5337abd..f98872690 100644 --- a/pkg/api/handlers/generic/containers_create.go +++ b/pkg/api/handlers/generic/containers_create.go @@ -71,11 +71,7 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { return } - type ctrCreateResponse struct { - Id string `json:"Id"` - Warnings []string `json:"Warnings"` - } - response := ctrCreateResponse{ + response := ContainerCreateResponse{ Id: ctr.ID(), Warnings: []string{}} diff --git a/pkg/api/handlers/generic/containers_stats.go b/pkg/api/handlers/generic/containers_stats.go index 0c4efc1df..26c8efa15 100644 --- a/pkg/api/handlers/generic/containers_stats.go +++ b/pkg/api/handlers/generic/containers_stats.go @@ -43,28 +43,26 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { return } + // If the container isn't returning, then let's not bother and return + // immediately. state, err := ctnr.State() if err != nil { utils.InternalServerError(w, err) return } if state != define.ContainerStateRunning && !query.Stream { - utils.WriteJSON(w, http.StatusOK, &handlers.Stats{StatsJSON: docker.StatsJSON{ - Name: ctnr.Name(), - ID: ctnr.ID(), - }}) + utils.InternalServerError(w, define.ErrCtrStateInvalid) return } - var preRead time.Time - var preCPUStats docker.CPUStats - stats, err := ctnr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain Container %s stats", name)) return } + var preRead time.Time + var preCPUStats docker.CPUStats if query.Stream { preRead = time.Now() preCPUStats = docker.CPUStats{ @@ -78,25 +76,44 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { OnlineCPUs: 0, ThrottlingData: docker.ThrottlingData{}, } - time.Sleep(DefaultStatsPeriod) } - cgroupPath, _ := ctnr.CGroupPath() - cgroup, _ := cgroups.Load(cgroupPath) - for ok := true; ok; ok = query.Stream { - state, _ := ctnr.State() - if state != define.ContainerStateRunning { - time.Sleep(10 * time.Second) - continue + // Container stats + stats, err := ctnr.GetContainerStats(stats) + if err != nil { + utils.InternalServerError(w, err) + return + } + inspect, err := ctnr.Inspect(false) + if err != nil { + utils.InternalServerError(w, err) + return + } + // Cgroup stats + cgroupPath, err := ctnr.CGroupPath() + if err != nil { + utils.InternalServerError(w, err) + return + } + cgroup, err := cgroups.Load(cgroupPath) + if err != nil { + utils.InternalServerError(w, err) + return + } + cgroupStat, err := cgroup.Stat() + if err != nil { + utils.InternalServerError(w, err) + return } - stats, _ := ctnr.GetContainerStats(stats) - cgroupStat, _ := cgroup.Stat() - inspect, _ := ctnr.Inspect(false) - + // FIXME: network inspection does not yet work entirely net := make(map[string]docker.NetworkStats) - net[inspect.NetworkSettings.EndpointID] = docker.NetworkStats{ + networkName := inspect.NetworkSettings.EndpointID + if networkName == "" { + networkName = "network" + } + net[networkName] = docker.NetworkStats{ RxBytes: stats.NetInput, RxPackets: 0, RxErrors: 0, @@ -127,13 +144,6 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { IoTimeRecursive: nil, SectorsRecursive: nil, }, - NumProcs: 0, - StorageStats: docker.StorageStats{ - ReadCountNormalized: 0, - ReadSizeBytes: 0, - WriteCountNormalized: 0, - WriteSizeBytes: 0, - }, CPUStats: docker.CPUStats{ CPUUsage: docker.CPUUsage{ TotalUsage: cgroupStat.CPU.Usage.Total, @@ -174,17 +184,21 @@ func StatsContainer(w http.ResponseWriter, r *http.Request) { preRead = s.Read bits, err := json.Marshal(s.CPUStats) if err != nil { - logrus.Errorf("unable to marshal cpu stats: %q", err) + logrus.Errorf("Unable to marshal cpu stats: %q", err) } if err := json.Unmarshal(bits, &preCPUStats); err != nil { - logrus.Errorf("unable to unmarshal previous stats: %q", err) + logrus.Errorf("Unable to unmarshal previous stats: %q", err) + } + + // Only sleep when we're streaming. + if query.Stream { + time.Sleep(DefaultStatsPeriod) } - time.Sleep(DefaultStatsPeriod) } } func toBlkioStatEntry(entries []cgroups.BlkIOEntry) []docker.BlkioStatEntry { - results := make([]docker.BlkioStatEntry, 0, len(entries)) + results := make([]docker.BlkioStatEntry, len(entries)) for i, e := range entries { bits, err := json.Marshal(e) if err != nil { diff --git a/pkg/api/handlers/generic/images.go b/pkg/api/handlers/generic/images.go index 8029ee861..395f64064 100644 --- a/pkg/api/handlers/generic/images.go +++ b/pkg/api/handlers/generic/images.go @@ -350,7 +350,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) return } - var summaries = make([]*handlers.ImageSummary, len(images)) + var summaries = make([]*handlers.ImageSummary, len(images)+1) for j, img := range images { is, err := handlers.ImageToImageSummary(img) if err != nil { diff --git a/pkg/api/handlers/generic/swagger.go b/pkg/api/handlers/generic/swagger.go new file mode 100644 index 000000000..27e1fc18d --- /dev/null +++ b/pkg/api/handlers/generic/swagger.go @@ -0,0 +1,23 @@ +package generic + +// Create container +// swagger:response ContainerCreateResponse +type swagCtrCreateResponse struct { + // in:body + Body struct { + ContainerCreateResponse + } +} + +// Wait container +// swagger:response ContainerWaitResponse +type swagCtrWaitResponse struct { + // in:body + Body struct { + // container exit code + StatusCode int + Error struct { + Message string + } + } +} diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index bfb028b1b..388be24b6 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -30,11 +30,6 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) { } func RemoveContainer(w http.ResponseWriter, r *http.Request) { - // 204 no error - // 400 bad param - // 404 no such container - // 409 conflict - // 500 internal error decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Force bool `schema:"force"` @@ -143,9 +138,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { // timestamps // tail string } -func StatsContainer(w http.ResponseWriter, r *http.Request) { - //stream -} + func CreateContainer(w http.ResponseWriter, r *http.Request) { } diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go index b677a5a0b..c845c8195 100644 --- a/pkg/api/handlers/swagger.go +++ b/pkg/api/handlers/swagger.go @@ -57,9 +57,7 @@ type swagLibpodInspectImageResponse struct { // swagger:response DocsContainerPruneReport type swagContainerPruneReport struct { // in: body - Body struct { - ContainersPruneReport - } + Body []ContainersPruneReport } // Inspect container @@ -84,9 +82,7 @@ type swagDockerTopResponse struct { // swagger:response LibpodListContainersResponse type swagLibpodListContainersResponse struct { // in:body - Body struct { - shared.PsContainerOutput - } + Body []shared.PsContainerOutput } // Inspect container diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 0815e6eca..65698bfd3 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -12,19 +12,19 @@ import ( // WriteResponse encodes the given value as JSON or string and renders it for http client func WriteResponse(w http.ResponseWriter, code int, value interface{}) { - switch value.(type) { + switch v := value.(type) { case string: w.Header().Set("Content-Type", "text/plain; charset=us-ascii") w.WriteHeader(code) - if _, err := fmt.Fprintln(w, value); err != nil { + if _, err := fmt.Fprintln(w, v); err != nil { log.Errorf("unable to send string response: %q", err) } case *os.File: w.Header().Set("Content-Type", "application/octet; charset=us-ascii") w.WriteHeader(code) - if _, err := io.Copy(w, value.(*os.File)); err != nil { + if _, err := io.Copy(w, v); err != nil { log.Errorf("unable to copy to response: %q", err) } default: @@ -33,6 +33,7 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) { } func WriteJSON(w http.ResponseWriter, code int, value interface{}) { + // FIXME: we don't need to write the header in all/some circumstances. w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index af1881624..8b0b816cd 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -10,58 +10,80 @@ import ( ) func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { - // swagger:operation POST /containers/create containers createContainer - // - // Create a container - // + // swagger:operation POST /containers/create compat containerCreate // --- - // produces: - // - application/json + // summary: Create a container + // tags: + // - containers (compat) + // produces: + // - application/json + // parameters: + // - in: query + // name: name + // type: string + // description: container name + // responses: + // '201': + // $ref: "#/responses/ContainerCreateResponse" + // '400': + // "$ref": "#/responses/BadParamError" + // '404': + // "$ref": "#/responses/NoSuchContainer" + // '409': + // "$ref": "#/responses/ConflictError" + // '500': + // "$ref": "#/responses/InternalError" + r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, generic.CreateContainer)).Methods(http.MethodPost) + // swagger:operation GET /containers/json compat listContainers + // --- + // tags: + // - containers (compat) + // summary: List containers + // description: Returns a list of containers // parameters: // - in: query - // name: name + // name: filters // type: string - // description: container name - // responses: - // '201': - // description: tbd - // '400': - // "$ref": "#/responses/BadParamError" - // '404': - // "$ref": "#/responses/NoSuchContainer" - // '409': - // "$ref": "#/types/ConflictError" - // '500': - // "$ref": "#/types/InternalError" - r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, generic.CreateContainer)).Methods(http.MethodPost) - // swagger:operation GET /containers/json containers listContainers - // - // List containers - // - // --- + // description: | + // Returns a list of containers. + // - ancestor=(<image-name>[:<tag>], <image id>, or <image@digest>) + // - before=(<container id> or <container name>) + // - expose=(<port>[/<proto>]|<startport-endport>/[<proto>]) + // - exited=<int> containers with exit code of <int> + // - health=(starting|healthy|unhealthy|none) + // - id=<ID> a container's ID + // - is-task=(true|false) + // - label=key or label="key=value" of a container label + // - name=<name> a container's name + // - network=(<network id> or <network name>) + // - publish=(<port>[/<proto>]|<startport-endport>/[<proto>]) + // - since=(<container id> or <container name>) + // - status=(created|restarting|running|removing|paused|exited|dead) + // - volume=(<volume name> or <mount point destination>) // produces: // - application/json // responses: // '200': - // schema: - // type: array - // items: - // "$ref": "#/responses/Container" + // "$ref": "#/responses/DocsListContainer" // '400': // "$ref": "#/responses/BadParamError" // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/json"), APIHandler(s.Context, generic.ListContainers)).Methods(http.MethodGet) - // swagger:operation POST /containers/prune containers pruneContainers - // - // Prune unused containers - // + // swagger:operation POST /containers/prune compat pruneContainers // --- + // tags: + // - containers (compat) + // summary: Delete stopped containers + // description: Remove containers not in use // parameters: // - in: query // name: filters - // type: map[string][]string - // description: something + // type: string + // description: | + // Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + // - `until=<timestamp>` Prune containers created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune containers with (or without, in case `label!=...` is used) the specified labels. // produces: // - application/json // responses: @@ -70,11 +92,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/prune"), APIHandler(s.Context, generic.PruneContainers)).Methods(http.MethodPost) - // swagger:operation DELETE /containers/{nameOrID} containers removeContainer - // - // Delete container - // + // swagger:operation DELETE /containers/{nameOrID} compat removeContainer // --- + // tags: + // - containers (compat) + // summary: Remove a container // parameters: // - in: path // name: nameOrID @@ -83,11 +105,13 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // - in: query // name: force // type: bool - // description: need something + // default: false + // description: If the container is running, kill it before removing it. // - in: query // name: v // type: bool - // description: need something + // default: false + // description: Remove the volumes associated with the container. // - in: query // name: link // type: bool @@ -106,16 +130,22 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}"), APIHandler(s.Context, generic.RemoveContainer)).Methods(http.MethodDelete) - // swagger:operation GET /containers/{nameOrID}/json containers getContainer - // - // Inspect Container - // + // swagger:operation GET /containers/{nameOrID}/json compat getContainer // --- + // tags: + // - containers (compat) + // summary: Inspect container + // description: Return low-level information about a container. // parameters: // - in: path // name: nameOrID // required: true - // description: the name or ID of the container + // description: the name or id of the container + // - in: query + // name: size + // type: bool + // default: false + // description: include the size of the container // produces: // - application/json // responses: @@ -126,11 +156,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/json"), APIHandler(s.Context, generic.GetContainer)).Methods(http.MethodGet) - // swagger:operation POST /containers/{nameOrID}/kill containers killContainer - // - // Kill Container - // + // swagger:operation post /containers/{nameOrID}/kill compat killcontainer // --- + // tags: + // - containers (compat) + // summary: Kill container + // description: Signal to send to the container as an integer or string (e.g. SIGINT) // parameters: // - in: path // name: nameOrID @@ -152,11 +183,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/kill"), APIHandler(s.Context, generic.KillContainer)).Methods(http.MethodPost) - // swagger:operation GET /containers/{nameOrID}/logs containers LogsFromContainer - // - // Get logs from container - // + // swagger:operation GET /containers/{nameOrID}/logs compat LogsFromContainer // --- + // tags: + // - containers (compat) + // summary: Get container logs + // description: Get stdout and stderr logs from a container. // parameters: // - in: path // name: nameOrID @@ -165,46 +197,49 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // - in: query // name: follow // type: bool - // description: needs description + // description: Keep connection after returning logs. // - in: query // name: stdout // type: bool - // description: needs description + // description: not supported // - in: query // name: stderr // type: bool - // description: needs description + // description: not supported? // - in: query // name: since // type: string - // description: needs description + // description: Only return logs since this time, as a UNIX timestamp // - in: query // name: until // type: string - // description: needs description + // description: Only return logs before this time, as a UNIX timestamp // - in: query // name: timestamps // type: bool - // description: needs description + // default: false + // description: Add timestamps to every log line // - in: query // name: tail // type: string - // description: needs description + // description: Only return this number of log lines from the end of the logs + // default: all // produces: // - application/json // responses: // '200': - // description: tbd + // description: logs returned as a stream in response body. // '404': // "$ref": "#/responses/NoSuchContainer" // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/logs"), APIHandler(s.Context, generic.LogsFromContainer)).Methods(http.MethodGet) - // swagger:operation POST /containers/{nameOrID}/pause containers pauseContainer - // - // Pause Container - // + // swagger:operation POST /containers/{nameOrID}/pause compat pauseContainer // --- + // tags: + // - containers (compat) + // summary: Pause container + // description: Use the cgroups freezer to suspend all processes in a container. // parameters: // - in: path // name: nameOrID @@ -221,11 +256,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost) r.HandleFunc(VersionedPath("/containers/{name:..*}/rename"), APIHandler(s.Context, handlers.UnsupportedHandler)).Methods(http.MethodPost) - // swagger:operation POST /containers/{nameOrID}/restart containers restartContainer - // - // Restart Container - // + // swagger:operation POST /containers/{nameOrID}/restart compat restartContainer // --- + // tags: + // - containers (compat) + // summary: Restart container // parameters: // - in: path // name: nameOrID @@ -245,11 +280,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost) - // swagger:operation POST /containers/{nameOrID}/start containers startContainer - // - // Start a container - // + // swagger:operation POST /containers/{nameOrID}/start compat startContainer // --- + // tags: + // - containers (compat) + // summary: Start a container // parameters: // - in: path // name: nameOrID @@ -271,11 +306,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost) - // swagger:operation GET /containers/{nameOrID}/stats containers statsContainer - // - // Get stats for a container - // + // swagger:operation GET /containers/{nameOrID}/stats compat statsContainer // --- + // tags: + // - containers (compat) + // summary: Get stats for a container + // description: This returns a live stream of a container’s resource usage statistics. // parameters: // - in: path // name: nameOrID @@ -284,24 +320,23 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // - in: query // name: stream // type: bool - // description: needs description + // default: true + // description: Stream the output // produces: // - application/json // responses: - // '204': - // description: tbd - // '304': - // "$ref": "#/responses/ContainerAlreadyStartedError" + // '200': + // description: no error // '404': // "$ref": "#/responses/NoSuchContainer" // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet) - // swagger:operation POST /containers/{nameOrID}/stop containers stopContainer - // - // Stop a container - // + // swagger:operation POST /containers/{nameOrID}/stop compat stopContainer // --- + // tags: + // - containers (compat) + // summary: Stop a container // parameters: // - in: path // name: nameOrID @@ -323,11 +358,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost) - // swagger:operation GET /containers/{nameOrID}/top containers topContainer - // - // List processes running inside a container - // + // swagger:operation GET /containers/{nameOrID}/top compat topContainer // --- + // tags: + // - containers (compat) + // summary: List processes running inside a container // parameters: // - in: path // name: nameOrID @@ -347,11 +382,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet) - // swagger:operation POST /containers/{nameOrID}/unpause containers unpauseContainer - // - // Unpause Container - // + // swagger:operation POST /containers/{nameOrID}/unpause compat unpauseContainer // --- + // tags: + // - containers (compat) + // summary: Unpause container + // description: Resume a paused container // parameters: // - in: path // name: nameOrID @@ -362,17 +398,17 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // responses: // '204': // description: no error - // schema: // '404': // "$ref": "#/responses/NoSuchContainer" // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name:..*}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost) - // swagger:operation POST /containers/{nameOrID}/wait containers waitContainer - // - // Wait on a container to exit - // + // swagger:operation POST /containers/{nameOrID}/wait compat waitContainer // --- + // tags: + // - containers (compat) + // summary: Wait on a container to exit + // description: Block until a container stops, then returns the exit code. // parameters: // - in: path // name: nameOrID @@ -385,8 +421,8 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // produces: // - application/json // responses: - // '204': - // description: no error + // '200': + // $ref: "#/responses/ContainerWaitResponse" // '404': // "$ref": "#/responses/NoSuchContainer" // '500': @@ -398,29 +434,29 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { */ r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, libpod.CreateContainer)).Methods(http.MethodPost) - // swagger:operation GET /libpod/containers/json containers listContainers - // - // List containers - // + // swagger:operation GET /libpod/containers/json libpod libpodListContainers // --- + // tags: + // - containers + // summary: List containers + // description: Returns a list of containers // produces: // - application/json // responses: // '200': // schema: - // type: array - // items: // "$ref": "#/responses/LibpodListContainersResponse" // '400': // "$ref": "#/responses/BadParamError" // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/json"), APIHandler(s.Context, libpod.ListContainers)).Methods(http.MethodGet) - // swagger:operation POST /libpod/containers/prune containers pruneContainers - // - // Prune unused containers - // + // swagger:operation POST /libpod/containers/prune libpod libpodPruneContainers // --- + // tags: + // - containers + // summary: Prune unused containers + // description: Remove stopped and exited containers // parameters: // - in: query // name: force @@ -428,21 +464,25 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // description: something // - in: query // name: filters - // type: map[string][]string - // description: something + // type: string + // description: | + // Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters: + // - `until=<timestamp>` Prune containers created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune containers with (or without, in case `label!=...` is used) the specified labels. // produces: // - application/json // responses: // '200': - // description: TBD + // description: to be determined // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/prune"), APIHandler(s.Context, libpod.PruneContainers)).Methods(http.MethodPost) - // swagger:operation GET /libpod/containers/showmounted containers showMounterContainers - // - // Show mounted containers - // + // swagger:operation GET /libpod/containers/showmounted libpod showMounterContainers // --- + // tags: + // - containers + // summary: Show mounted containers + // description: Lists all mounted containers mount points // produces: // - application/json // responses: @@ -455,11 +495,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/showmounted"), APIHandler(s.Context, libpod.ShowMountedContainers)).Methods(http.MethodGet) - // swagger:operation DELETE /libpod/containers/json containers removeContainer - // - // Delete container - // + // swagger:operation DELETE /libpod/containers/json libpod libpodRemoveContainer // --- + // tags: + // - containers + // summary: Delete container // parameters: // - in: path // name: nameOrID @@ -472,7 +512,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // - in: query // name: v // type: bool - // description: need something + // description: delete volumes // produces: // - application/json // responses: @@ -487,11 +527,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}"), APIHandler(s.Context, libpod.RemoveContainer)).Methods(http.MethodDelete) - // swagger:operation GET /libpod/containers/{nameOrID}/json containers getContainer - // - // Inspect Container - // + // swagger:operation GET /libpod/containers/{nameOrID}/json libpod libpodGetContainer // --- + // tags: + // - containers + // summary: Inspect container + // description: Return low-level information about a container. // parameters: // - in: path // name: nameOrID @@ -511,11 +552,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/json"), APIHandler(s.Context, libpod.GetContainer)).Methods(http.MethodGet) - // swagger:operation POST /libpod/containers/{nameOrID}/kill containers killContainer - // - // Kill Container - // + // swagger:operation POST /libpod/containers/{nameOrID}/kill libpod libpodKillContainer // --- + // tags: + // - containers + // summary: Kill container + // description: send a signal to a container, defaults to killing the container // parameters: // - in: path // name: nameOrID @@ -538,11 +580,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/kill"), APIHandler(s.Context, libpod.KillContainer)).Methods(http.MethodGet) - // swagger:operation GET /libpod/containers/{nameOrID}/mount containers mountContainer - // - // Mount a container - // + // swagger:operation GET /libpod/containers/{nameOrID}/mount libpod mountContainer // --- + // tags: + // - containers + // summary: Mount a container + // description: Mount a container to the filesystem // parameters: // - in: path // name: nameOrID @@ -563,11 +606,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/mount"), APIHandler(s.Context, libpod.MountContainer)).Methods(http.MethodPost) r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/logs"), APIHandler(s.Context, libpod.LogsFromContainer)).Methods(http.MethodGet) - // swagger:operation POST /libpod/containers/{nameOrID}/pause containers pauseContainer - // - // Pause Container - // + // swagger:operation POST /libpod/containers/{nameOrID}/pause libpod libpodPauseContainer // --- + // tags: + // - containers + // summary: Pause a container + // description: Use the cgroups freezer to suspend all processes in a container. // parameters: // - in: path // name: nameOrID @@ -583,11 +627,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost) - // swagger:operation POST /libpod/containers/{nameOrID}/restart containers restartContainer - // - // Restart Container - // + // swagger:operation POST /libpod/containers/{nameOrID}/restart libpod libpodRestartContainer // --- + // tags: + // - containers + // summary: Restart a container // parameters: // - in: path // name: nameOrID @@ -607,11 +651,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost) - // swagger:operation POST /libpod/containers/{nameOrID}/start containers startContainer - // - // Start a container - // + // swagger:operation POST /libpod/containers/{nameOrID}/start libpod libpodStartContainer // --- + // tags: + // - containers + // summary: Start a container // parameters: // - in: path // name: nameOrID @@ -633,13 +677,38 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost) - r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stats"), APIHandler(s.Context, libpod.StatsContainer)).Methods(http.MethodGet) + // swagger:operation GET /libpod/containers/{nameOrID}/stats libpod statsContainer + // --- + // tags: + // - containers + // summary: Get stats for a container + // description: This returns a live stream of a container’s resource usage statistics. + // parameters: + // - in: path + // name: nameOrID + // required: true + // description: the name or ID of the container + // - in: query + // name: stream + // type: bool + // default: true + // description: Stream the output + // produces: + // - application/json + // responses: + // '200': + // description: no error + // '404': + // "$ref": "#/responses/NoSuchContainer" + // '500': + // "$ref": "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet) r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet) - // swagger:operation POST /libpod/containers/{nameOrID}/unpause containers unpauseContainer - // - // Unpause Container - // + // swagger:operation POST /libpod/containers/{nameOrID}/unpause libpod libpodUnpauseContainer // --- + // tags: + // - containers + // summary: Unpause Container // parameters: // - in: path // name: nameOrID @@ -655,11 +724,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost) - // swagger:operation POST /libpod/containers/{nameOrID}/wait containers waitContainer - // - // Wait on a container to exit - // + // swagger:operation POST /libpod/containers/{nameOrID}/wait libpod libpodWaitContainer // --- + // tags: + // - containers + // summary: Wait on a container to exit // parameters: // - in: path // name: nameOrID @@ -679,11 +748,12 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/wait"), APIHandler(s.Context, libpod.WaitContainer)).Methods(http.MethodPost) - // swagger:operation POST /libpod/containers/{nameOrID}/exists containers containerExists - // - // Check if container exists - // + // swagger:operation POST /libpod/containers/{nameOrID}/exists libpod containerExists // --- + // tags: + // - containers + // summary: Check if container exists + // description: Quick way to determine if a container exists by name or ID // parameters: // - in: path // name: nameOrID @@ -699,11 +769,11 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // '500': // "$ref": "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/exists"), APIHandler(s.Context, libpod.ContainerExists)).Methods(http.MethodGet) - // swagger:operation POST /libpod/containers/{nameOrID}/stop containers stopContainer - // - // Stop a container - // + // swagger:operation POST /libpod/containers/{nameOrID}/stop libpod libpodStopContainer // --- + // tags: + // - containers + // summary: Stop a container // parameters: // - in: path // name: nameOrID diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go index d764fdbb4..56cf96de1 100644 --- a/pkg/api/server/register_events.go +++ b/pkg/api/server/register_events.go @@ -26,7 +26,7 @@ func (s *APIServer) RegisterEventsHandlers(r *mux.Router) error { // description: OK // "500": // description: Failed - // "$ref": "#/types/errorModel" + // "$ref": "#/responses/InternalError" r.Handle(VersionedPath("/events"), APIHandler(s.Context, handlers.GetEvents)) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 488427f3c..cd42afe71 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -10,11 +10,13 @@ import ( ) func (s *APIServer) registerImagesHandlers(r *mux.Router) error { - // swagger:operation POST /images/create images createImage - // - // Create an image from an image + // swagger:operation POST /images/create compat createImage // // --- + // tags: + // - images (compat) + // summary: Create an image from an image + // description: Create an image by either pulling it from a registry or importing it. // produces: // - application/json // parameters: @@ -30,21 +32,22 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '200': // schema: // items: - // $ref: "TBD" + // $ref: "to be determined" // '404': // description: repo or image does not exist // schema: - // $ref: "#/responses/generalError" + // $ref: "#/responses/InternalError" // '500': // description: unexpected error // schema: // $ref: '#/responses/GenericError' r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromImage)).Methods(http.MethodPost).Queries("fromImage", "{fromImage}") - // swagger:operation POST /images/create images createImage - // - // Create an image from Source - // + // swagger:operation POST /images/create compat createImage // --- + // tags: + // - images (compat) + // summary: Create an image from Source + // description: Create an image by either pulling it from a registry or importing it. // produces: // - application/json // parameters: @@ -54,27 +57,28 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: needs description // - in: query // name: changes - // type: TBD + // type: to be determined // description: needs description // responses: // '200': // schema: // items: - // $ref: "TBD" + // $ref: "to be determined" // '404': // description: repo or image does not exist // schema: - // $ref: "#/responses/generalError" + // $ref: "#/responses/InternalError" // '500': // description: unexpected error // schema: // $ref: '#/responses/GenericError' r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromSrc)).Methods(http.MethodPost).Queries("fromSrc", "{fromSrc}") - // swagger:operation GET /images/json images listImages - // - // List Images - // + // swagger:operation GET /images/json compat listImages // --- + // tags: + // - images (compat) + // summary: List Images + // description: Returns a list of images on the server. Note that it uses a different, smaller representation of an image than inspecting a single image. // produces: // - application/json // responses: @@ -87,34 +91,47 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet) - // swagger:operation POST /images/load images loadImage - // - // Import image + // swagger:operation POST /images/load compat loadImage // // --- + // tags: + // - images (compat) + // summary: Import image + // description: Load a set of images and tags into a repository. // parameters: // - in: query // name: quiet // type: bool // description: not supported + // - in: body + // description: tarball of container image + // type: string + // format: binary // produces: // - application/json // responses: // '200': - // description: TBD + // description: no error // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost) - // swagger:operation POST /images/prune images pruneImages - // - // Prune unused images - // + // swagger:operation POST /images/prune compat pruneImages // --- + // tags: + // - images (compat) + // summary: Prune unused images + // description: Remove images from local storage that are not being used by a container // parameters: // - in: query // name: filters - // type: map[string][]string - // description: not supported + // type: string + // description: | + // filters to apply to image pruning, encoded as JSON (map[string][]string). Available filters: + // - `dangling=<boolean>` When set to `true` (or `1`), prune only + // unused *and* untagged images. When set to `false` + // (or `0`), all unused images are pruned. + // - `until=<string>` Prune images created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune images with (or without, in case `label!=...` is used) the specified labels. // produces: // - application/json // responses: @@ -125,11 +142,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/prune"), APIHandler(s.Context, generic.PruneImages)).Methods(http.MethodPost) - // swagger:operation GET /images/search images searchImages - // - // Search images - // + // swagger:operation GET /images/search compat searchImages // --- + // tags: + // - images (compat) + // summary: Search images + // description: Search registries for an image // parameters: // - in: query // name: term @@ -141,8 +159,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: maximum number of results // - in: query // name: filters - // type: map[string][]string - // description: TBD + // type: string + // description: | + // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + // - `is-automated=(true|false)` + // - `is-official=(true|false)` + // - `stars=<number>` Matches images that has at least 'number' stars. // produces: // - application/json // responses: @@ -151,11 +173,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet) - // swagger:operation DELETE /images/{nameOrID} images removeImage - // - // Remove Image - // + // swagger:operation DELETE /images/{nameOrID} compat removeImage // --- + // tags: + // - images (compat) + // summary: Remove Image + // description: Delete an image from local storage // parameters: // - in: query // name: force @@ -164,24 +187,25 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: noprune // type: bool - // description: not supported + // description: not supported. will be logged as an invalid parameter if enabled // produces: // - application/json // responses: // '200': // $ref: "#/responses/DocsImageDeleteResponse" - // '404': + // '400': // $ref: '#/responses/BadParamError' // '409': // $ref: '#/responses/ConflictError' // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/{name:..*}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) - // swagger:operation GET /images/{nameOrID}/get images exportImage - // - // Export an image - // + // swagger:operation GET /images/{nameOrID}/get compat exportImage // --- + // tags: + // - images (compat) + // summary: Export an image + // description: Export an image in tarball format // parameters: // - in: path // name: nameOrID @@ -191,17 +215,19 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - application/json // responses: // '200': + // description: no error // schema: - // $ref: "TBD" - // description: TBD + // type: string + // format: binary // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/{name:..*}/get"), APIHandler(s.Context, generic.ExportImage)).Methods(http.MethodGet) - // swagger:operation GET /images/{nameOrID}/history images imageHistory - // - // History of an image - // + // swagger:operation GET /images/{nameOrID}/history compat imageHistory // --- + // tags: + // - images (compat) + // summary: History of an image + // description: Return parent layers of an image. // parameters: // - in: path // name: nameOrID @@ -217,11 +243,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/images/{name:..*}/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet) - // swagger:operation GET /images/{nameOrID}/json images inspectImage - // - // Inspect an image - // + // swagger:operation GET /images/{nameOrID}/json compat inspectImage // --- + // tags: + // - images (compat) + // summary: Inspect an image + // description: Return low-level information about an image. // parameters: // - in: path // name: nameOrID @@ -237,11 +264,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/images/{name:..*}/json"), APIHandler(s.Context, generic.GetImage)) - // swagger:operation POST /images/{nameOrID}/tag images tagImage - // - // Tag an image - // + // swagger:operation POST /images/{nameOrID}/tag compat tagImage // --- + // tags: + // - images (compat) + // summary: Tag an image + // description: Tag an image so that it becomes part of a repository. // parameters: // - in: path // name: nameOrID @@ -269,11 +297,11 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/{name:..*}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost) - // swagger:operation POST /commit/ commit commitContainer - // - // Create a new image from a container - // + // swagger:operation POST /commit/ compat commitContainer // --- + // tags: + // - commit (compat) + // summary: Create a new image from a container // parameters: // - in: query // name: container @@ -318,11 +346,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { libpod endpoints */ - // swagger:operation POST /libpod/images/{nameOrID}/exists images imageExists - // - // Check if image exists in local store - // + // swagger:operation POST /libpod/images/{nameOrID}/exists libpod libpodImageExists // --- + // tags: + // - images + // summary: Image exists + // description: Check if image exists in local store // parameters: // - in: path // name: nameOrID @@ -330,15 +359,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: the name or ID of the container // produces: // - application/json - // parameters: - // - in: query - // name: fromImage - // type: string - // description: needs description - // - in: query - // name: tag - // type: string - // description: needs description // responses: // '204': // description: image exists @@ -348,11 +368,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:..*}/exists"), APIHandler(s.Context, libpod.ImageExists)) r.Handle(VersionedPath("/libpod/images/{name:..*}/tree"), APIHandler(s.Context, libpod.ImageTree)) - // swagger:operation GET /libpod/images/{nameOrID}/history images imageHistory - // - // History of an image - // + // swagger:operation GET /libpod/images/{nameOrID}/history libpod libpodImageHistory // --- + // tags: + // - images + // summary: History of an image + // description: Return parent layers of an image. // parameters: // - in: path // name: nameOrID @@ -370,49 +391,60 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet) - // swagger:operation GET /libpod/images/json images listImages - // - // List Images - // + // swagger:operation GET /libpod/images/json libpod libpodListImages // --- + // tags: + // - images + // summary: List Images + // description: Returns a list of images on the server // produces: // - application/json // responses: // '200': - // schema: - // items: // $ref: "#/responses/DockerImageSummary" // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/json"), APIHandler(s.Context, libpod.GetImages)).Methods(http.MethodGet) - // swagger:operation POST /libpod/images/load images loadImage - // - // Import image - // + // swagger:operation POST /libpod/images/load libpod libpodLoadImage // --- + // tags: + // - images + // summary: Import image + // description: Load a set of images and tags into a repository. // parameters: // - in: query // name: quiet // type: bool // description: not supported + // - in: body + // description: tarball of container image + // type: string + // format: binary // produces: // - application/json // responses: // '200': - // description: TBD + // description: no error // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, handlers.LoadImage)).Methods(http.MethodPost) - // swagger:operation POST /libpod/images/prune images pruneImages - // - // Prune unused images - // + // swagger:operation POST /libpod/images/prune libpod libpodPruneImages // --- + // tags: + // - images + // summary: Prune unused images + // description: Remove images that are not being used by a container // parameters: // - in: query // name: filters - // type: map[string][]string - // description: image filters + // type: string + // description: | + // filters to apply to image pruning, encoded as JSON (map[string][]string). Available filters: + // - `dangling=<boolean>` When set to `true` (or `1`), prune only + // unused *and* untagged images. When set to `false` + // (or `0`), all unused images are pruned. + // - `until=<string>` Prune images created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune images with (or without, in case `label!=...` is used) the specified labels. // - in: query // name: all // type: bool @@ -421,17 +453,17 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - application/json // responses: // '200': - // schema: // items: // $ref: "#/responses/DocsImageDeleteResponse" // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/prune"), APIHandler(s.Context, libpod.PruneImages)).Methods(http.MethodPost) - // swagger:operation GET /libpod/images/search images searchImages - // - // Search images - // + // swagger:operation GET /libpod/images/search libpod libpodSearchImages // --- + // tags: + // - images + // summary: Search images + // description: Search registries for images // parameters: // - in: query // name: term @@ -443,8 +475,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: maximum number of results // - in: query // name: filters - // type: map[string][]string - // description: TBD + // type: string + // description: | + // A JSON encoded value of the filters (a `map[string][]string`) to process on the images list. Available filters: + // - `is-automated=(true|false)` + // - `is-official=(true|false)` + // - `stars=<number>` Matches images that has at least 'number' stars. // produces: // - application/json // responses: @@ -455,20 +491,17 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet) - // swagger:operation DELETE /libpod/images/{nameOrID} images removeImage - // - // Remove Image - // + // swagger:operation DELETE /libpod/images/{nameOrID} libpod libpodRemoveImage // --- + // tags: + // - images + // summary: Remove Image + // description: Delete an image from local store // parameters: // - in: query // name: force // type: bool // description: remove the image even if used by containers or has other tags - // - in: query - // name: noprune - // type: bool - // description: not supported // produces: // - application/json // responses: @@ -476,6 +509,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // schema: // items: // $ref: "#/responses/DocsIageDeleteResponse" + // '400': + // $ref: "#/responses/BadParamError" // '404': // $ref: '#/responses/NoSuchImage' // '409': @@ -483,11 +518,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:..*}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) - // swagger:operation GET /libpod/images/{nameOrID}/get images exportImage - // - // Export an image - // + // swagger:operation GET /libpod/images/{nameOrID}/get libpod libpoodExportImage // --- + // tags: + // - images + // summary: Export an image + // description: Export an image as a tarball // parameters: // - in: path // name: nameOrID @@ -505,17 +541,21 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - application/json // responses: // '200': - // description: TBD + // description: no error + // schema: + // type: string + // format: binary // '404': // $ref: '#/responses/NoSuchImage' // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:..*}/get"), APIHandler(s.Context, libpod.ExportImage)).Methods(http.MethodGet) - // swagger:operation GET /libpod/images/{nameOrID}/json images inspectImage - // - // Inspect an image - // + // swagger:operation GET /libpod/images/{nameOrID}/json libpod libpodInspectImage // --- + // tags: + // - images + // summary: Inspect an image + // description: Obtain low-level information about an image // parameters: // - in: path // name: nameOrID @@ -525,19 +565,18 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - application/json // responses: // '200': - // schema: - // items: // $ref: "#/responses/DocsLibpodInspectImageResponse" // '404': // $ref: '#/responses/NoSuchImage' // '500': // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:..*}/json"), APIHandler(s.Context, libpod.GetImage)) - // swagger:operation POST /libpod/images/{nameOrID}/tag images tagImage - // - // Tag an image - // + // swagger:operation POST /libpod/images/{nameOrID}/tag libpod libpodTagImage // --- + // tags: + // - images + // summary: Tag an image + // description: Tag an image so that it becomes part of a repository. // parameters: // - in: path // name: nameOrID diff --git a/pkg/api/server/register_info.go b/pkg/api/server/register_info.go index 797158553..a7fb18721 100644 --- a/pkg/api/server/register_info.go +++ b/pkg/api/server/register_info.go @@ -8,21 +8,17 @@ import ( ) func (s *APIServer) registerInfoHandlers(r *mux.Router) error { - // swagger:operation GET /info libpod getInfo - // - // Returns information on the system and libpod configuration - // + // swagger:operation GET /info libpod libpodGetInfo // --- + // summary: Get info + // description: Returns information on the system and libpod configuration // produces: // - application/json // responses: // '200': - // schema: - // "$ref": "#/types/Info" + // description: to be determined // '500': - // description: unexpected error - // schema: - // "$ref": "#/types/ErrorModel" + // "$ref": "#/responses/InternalError" r.Handle(VersionedPath("/info"), APIHandler(s.Context, generic.GetInfo)).Methods(http.MethodGet) return nil } diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index c2e10277e..5069326b6 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -9,10 +9,8 @@ import ( func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // swagger:operation GET /libpod/pods/json pods ListPods - // - // List Pods - // // --- + // summary: List pods // produces: // - application/json // parameters: @@ -21,23 +19,25 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // descriptions: needs description and plumbing for filters // responses: // '200': - // $ref: "#/responses/ListPodsResponse" + // properties: + // items: + // $ref: "#/responses/ListPodsResponse" + // type: array // '400': // $ref: "#/responses/BadParamError" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/json"), APIHandler(s.Context, libpod.Pods)).Methods(http.MethodGet) r.Handle(VersionedPath("/libpod/pods/create"), APIHandler(s.Context, libpod.PodCreate)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/prune pods PrunePods - // - // Prune unused pods - // // --- + // summary: Prune unused pods // parameters: // - in: query // name: force // description: force delete // type: bool + // default: false // produces: // - application/json // responses: @@ -46,13 +46,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '400': // $ref: "#/responses/BadParamError" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/prune"), APIHandler(s.Context, libpod.PodPrune)).Methods(http.MethodPost) // swagger:operation DELETE /libpod/pods/{nameOrID} pods removePod - // - // Remove Pod - // // --- + // summary: Remove pod // produces: // - application/json // parameters: @@ -72,13 +70,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}"), APIHandler(s.Context, libpod.PodDelete)).Methods(http.MethodDelete) // swagger:operation GET /libpod/pods/{nameOrID}/json pods inspectPod - // - // Inspect Pod - // // --- + // summary: Inspect pod // produces: // - application/json // parameters: @@ -92,13 +88,12 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/json"), APIHandler(s.Context, libpod.PodInspect)).Methods(http.MethodGet) // swagger:operation GET /libpod/pods/{nameOrID}/exists pods podExists - // - // Inspect Pod - // // --- + // summary: Pod exists + // description: Check if a pod exists by name or ID // produces: // - application/json // parameters: @@ -112,13 +107,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/exists"), APIHandler(s.Context, libpod.PodExists)).Methods(http.MethodGet) // swagger:operation POST /libpod/pods/{nameOrID}/kill pods killPod - // - // Kill a pod - // // --- + // summary: Kill a pod // produces: // - application/json // parameters: @@ -140,13 +133,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '409': // $ref: "#/responses/ConflictError" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/kill"), APIHandler(s.Context, libpod.PodKill)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{nameOrID}/pause pods pausePod - // - // Pause a pod - // // --- + // summary: Pause a pod // produces: // - application/json // parameters: @@ -160,13 +151,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/pause"), APIHandler(s.Context, libpod.PodPause)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{nameOrID}/restart pods restartPod - // - // Restart a pod - // // --- + // summary: Restart a pod // produces: // - application/json // parameters: @@ -180,13 +169,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/restart"), APIHandler(s.Context, libpod.PodRestart)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{nameOrID}/start pods startPod - // - // Start a pod - // // --- + // summary: Start a pod // produces: // - application/json // parameters: @@ -202,13 +189,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/start"), APIHandler(s.Context, libpod.PodStart)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{nameOrID}/stop pods stopPod - // - // Stop a pod - // // --- + // summary: Stop a pod // produces: // - application/json // parameters: @@ -230,13 +215,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/stop"), APIHandler(s.Context, libpod.PodStop)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{nameOrID}/unpause pods unpausePod - // - // Unpause a pod - // // --- + // summary: Unpause a pod // produces: // - application/json // parameters: @@ -250,7 +233,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // '404': // $ref: "#/responses/NoSuchPod" // '500': - // $ref: "#responses/InternalError" + // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name:..*}/unpause"), APIHandler(s.Context, libpod.PodUnpause)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go index 8fe5a67e4..34138cfbf 100644 --- a/pkg/api/server/register_volumes.go +++ b/pkg/api/server/register_volumes.go @@ -9,10 +9,8 @@ import ( func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // swagger:operation POST /libpod/volumes/create volumes createVolume - // - // Create a volume - // // --- + // summary: Create a volume // produces: // - application/json // responses: @@ -23,10 +21,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { r.Handle("/libpod/volumes/create", APIHandler(s.Context, libpod.CreateVolume)).Methods(http.MethodPost) r.Handle("/libpod/volumes/json", APIHandler(s.Context, libpod.ListVolumes)).Methods(http.MethodGet) // swagger:operation POST /volumes/prune volumes pruneVolumes - // - // Prune volumes - // // --- + // summary: Prune volumes // produces: // - application/json // responses: @@ -36,10 +32,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // "$ref": "#/responses/InternalError" r.Handle("/libpod/volumes/prune", APIHandler(s.Context, libpod.PruneVolumes)).Methods(http.MethodPost) // swagger:operation GET /volumes/{nameOrID}/json volumes inspectVolume - // - // Inspect volume - // // --- + // summary: Inspect volume // parameters: // - in: path // name: nameOrID @@ -56,10 +50,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // "$ref": "#/responses/InternalError" r.Handle("/libpod/volumes/{name:..*}/json", APIHandler(s.Context, libpod.InspectVolume)).Methods(http.MethodGet) // swagger:operation DELETE /volumes/{nameOrID} volumes removeVolume - // - // Inspect volume - // // --- + // summary: Remove volume // parameters: // - in: path // name: nameOrID diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 0eb57ebab..dbf499ce7 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -117,9 +117,7 @@ type swagPodAlreadyStopped struct { // swagger:response DockerImageSummary type swagImageSummary struct { // in:body - Body struct { - handlers.ImageSummary - } + Body []handlers.ImageSummary } // List Containers @@ -128,6 +126,11 @@ type swagListContainers struct { // in:body Body struct { // This causes go-swagger to crash - //handlers.Container + // handlers.Container } } + +// To be determined +// swagger:response tbd +type swagTBD struct { +} diff --git a/pkg/api/tags.yaml b/pkg/api/tags.yaml new file mode 100644 index 000000000..ad0de656f --- /dev/null +++ b/pkg/api/tags.yaml @@ -0,0 +1,13 @@ +tags: + - name: containers + description: Actions related to containers + - name: images + description: Actions related to images + - name: pods + description: Actions related to pods + - name: volumes + description: Actions related to volumes + - name: containers (compat) + description: Actions related to containers for the compatibility endpoints + - name: images (compat) + description: Actions related to images for the compatibility endpoints diff --git a/pkg/apparmor/apparmor_linux_test.go b/pkg/apparmor/apparmor_linux_test.go index e94293d87..3ff6e18bc 100644 --- a/pkg/apparmor/apparmor_linux_test.go +++ b/pkg/apparmor/apparmor_linux_test.go @@ -134,7 +134,7 @@ func TestDefaultContent(t *testing.T) { if _, err := os.Stat(aapath); err != nil { t.Skip("AppArmor isn't available in this environment") } - if err := DefaultContent(profile); err != nil { + if _, err := DefaultContent(profile); err != nil { t.Fatalf("Couldn't retrieve default AppArmor profile content '%s': %v", profile, err) } } diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go new file mode 100644 index 000000000..551a63c62 --- /dev/null +++ b/pkg/bindings/connection.go @@ -0,0 +1,62 @@ +package bindings + +import ( + "fmt" + "io" + "net/http" +) + +const ( + defaultConnection string = "http://localhost:8080/v1.24/libpod" + pingConnection string = "http://localhost:8080/_ping" +) + +type APIResponse struct { + *http.Response + Request *http.Request +} + +type Connection struct { + url string + client *http.Client +} + +func NewConnection(url string) (Connection, error) { + if len(url) < 1 { + url = defaultConnection + } + newConn := Connection{ + url: url, + client: &http.Client{}, + } + response, err := http.Get(pingConnection) + if err != nil { + return newConn, err + } + if err := response.Body.Close(); err != nil { + return newConn, err + } + return newConn, err +} + +func (c Connection) makeEndpoint(u string) string { + return fmt.Sprintf("%s%s", defaultConnection, u) +} + +func (c Connection) newRequest(httpMethod, endpoint string, httpBody io.Reader, params map[string]string) (*APIResponse, error) { + e := c.makeEndpoint(endpoint) + req, err := http.NewRequest(httpMethod, e, httpBody) + if err != nil { + return nil, err + } + if len(params) > 0 { + // if more desirable we could use url to form the encoded endpoint with params + r := req.URL.Query() + for k, v := range params { + r.Add(k, v) + } + req.URL.RawQuery = r.Encode() + } + response, err := c.client.Do(req) // nolint + return &APIResponse{response, req}, err +} diff --git a/pkg/bindings/containers.go b/pkg/bindings/containers.go index cd0b09767..01f68f970 100644 --- a/pkg/bindings/containers.go +++ b/pkg/bindings/containers.go @@ -109,12 +109,14 @@ func (c Connection) UnpauseContainer(nameOrID string) error { } func (c Connection) WaitContainer(nameOrID string) error { - _, err := http.Post(c.makeEndpoint(fmt.Sprintf("containers/%s/wait", nameOrID)), "application/json", nil) + // TODO when returns are ironed out, we can should use the newRequest approach + _, err := http.Post(c.makeEndpoint(fmt.Sprintf("containers/%s/wait", nameOrID)), "application/json", nil) // nolint return err } func (c Connection) ContainerExists(nameOrID string) (bool, error) { - response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/containers/%s/exists", nameOrID))) + response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/containers/%s/exists", nameOrID))) // nolint + defer closeResponseBody(response) if err != nil { return false, err } diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go new file mode 100644 index 000000000..9a02925a3 --- /dev/null +++ b/pkg/bindings/errors.go @@ -0,0 +1,46 @@ +package bindings + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +var ( + ErrNotImplemented = errors.New("function not implemented") +) + +func handleError(data []byte) error { + e := utils.ErrorModel{} + if err := json.Unmarshal(data, &e); err != nil { + return err + } + return e +} + +func (a APIResponse) Process(unmarshalInto interface{}) error { + data, err := ioutil.ReadAll(a.Response.Body) + if err != nil { + return errors.Wrap(err, "unable to process API response") + } + if a.Response.StatusCode == http.StatusOK { + if unmarshalInto != nil { + return json.Unmarshal(data, unmarshalInto) + } + return nil + } + // TODO should we add a debug here with the response code? + return handleError(data) +} + +func closeResponseBody(r *http.Response) { + if r != nil { + if err := r.Body.Close(); err != nil { + logrus.Error(errors.Wrap(err, "unable to close response body")) + } + } +} diff --git a/pkg/bindings/images.go b/pkg/bindings/images.go new file mode 100644 index 000000000..3abc8c372 --- /dev/null +++ b/pkg/bindings/images.go @@ -0,0 +1,111 @@ +package bindings + +import ( + "fmt" + "io" + "net/http" + "strconv" + + "github.com/containers/libpod/pkg/api/handlers" + "github.com/containers/libpod/pkg/inspect" +) + +func (c Connection) ImageExists(nameOrID string) (bool, error) { + response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/images/%s/exists", nameOrID))) // nolint + defer closeResponseBody(response) + if err != nil { + return false, err + } + if response.StatusCode == http.StatusOK { + return true, nil + } + return false, nil +} + +func (c Connection) ListImages() ([]handlers.ImageSummary, error) { + imageSummary := []handlers.ImageSummary{} + response, err := c.newRequest(http.MethodGet, "/images/json", nil, nil) + if err != nil { + return imageSummary, err + } + return imageSummary, response.Process(&imageSummary) +} + +func (c Connection) GetImage(nameOrID string) (*inspect.ImageData, error) { + inspectedData := inspect.ImageData{} + response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/json", nameOrID), nil, nil) + if err != nil { + return &inspectedData, err + } + return &inspectedData, response.Process(&inspectedData) +} + +func (c Connection) ImageTree(nameOrId string) error { + return ErrNotImplemented +} + +func (c Connection) ImageHistory(nameOrID string) ([]handlers.HistoryResponse, error) { + history := []handlers.HistoryResponse{} + response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/history", nameOrID), nil, nil) + if err != nil { + return history, err + } + return history, response.Process(&history) +} + +func (c Connection) LoadImage(r io.Reader) error { + // TODO this still needs error handling added + _, err := http.Post(c.makeEndpoint("/images/loads"), "application/json", r) //nolint + return err +} + +func (c Connection) RemoveImage(nameOrID string, force bool) ([]map[string]string, error) { + deletes := []map[string]string{} + params := make(map[string]string) + params["force"] = strconv.FormatBool(force) + response, err := c.newRequest(http.MethodDelete, fmt.Sprintf("/images/%s", nameOrID), nil, params) + if err != nil { + return nil, err + } + return deletes, response.Process(&deletes) +} + +func (c Connection) ExportImage(nameOrID string, w io.Writer, format string, compress bool) error { + params := make(map[string]string) + params["format"] = format + params["compress"] = strconv.FormatBool(compress) + response, err := c.newRequest(http.MethodGet, fmt.Sprintf("/images/%s/get", nameOrID), nil, params) + if err != nil { + return err + } + if err := response.Process(nil); err != nil { + return err + } + _, err = io.Copy(w, response.Body) + return err +} + +func (c Connection) PruneImages(all bool, filters []string) ([]string, error) { + var ( + deleted []string + ) + params := make(map[string]string) + // FIXME How do we do []strings? + //params["filters"] = format + response, err := c.newRequest(http.MethodPost, "/images/prune", nil, params) + if err != nil { + return deleted, err + } + return deleted, response.Process(nil) +} + +func (c Connection) TagImage(nameOrID string) error { + var () + response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/images/%s/tag", nameOrID), nil, nil) + if err != nil { + return err + } + return response.Process(nil) +} + +func (c Connection) BuildImage(nameOrId string) {} diff --git a/pkg/bindings/pods.go b/pkg/bindings/pods.go index eac9d2ef5..704d71477 100644 --- a/pkg/bindings/pods.go +++ b/pkg/bindings/pods.go @@ -14,7 +14,8 @@ func (c Connection) CreatePod() error { } func (c Connection) PodExists(nameOrID string) (bool, error) { - response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/pods/%s/exists", nameOrID))) + response, err := http.Get(c.makeEndpoint(fmt.Sprintf("/pods/%s/exists", nameOrID))) // nolint + defer closeResponseBody(response) if err != nil { return false, err } diff --git a/pkg/bindings/volumes.go b/pkg/bindings/volumes.go index 27e6f9efa..219f924e7 100644 --- a/pkg/bindings/volumes.go +++ b/pkg/bindings/volumes.go @@ -52,7 +52,7 @@ func (c Connection) PruneVolumes() ([]string, error) { func (c Connection) RemoveVolume(nameOrID string, force bool) error { params := make(map[string]string) params["force"] = strconv.FormatBool(force) - response, err := c.newRequest(http.MethodPost, fmt.Sprintf("/volumes/prune", nameOrID), nil, params) + response, err := c.newRequest(http.MethodPost, "/volumes/prune", nil, params) if err != nil { return err } diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 9711e8120..6b28b2759 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -155,7 +155,7 @@ func (c *CgroupControl) getCgroupv1Path(name string) string { } // createCgroupv2Path creates the cgroupv2 path and enables all the available controllers -func createCgroupv2Path(path string) (Err error) { +func createCgroupv2Path(path string) (deferredError error) { content, err := ioutil.ReadFile("/sys/fs/cgroup/cgroup.controllers") if err != nil { return errors.Wrapf(err, "read /sys/fs/cgroup/cgroup.controllers") @@ -169,7 +169,7 @@ func createCgroupv2Path(path string) (Err error) { if i == 0 { res = fmt.Sprintf("+%s", c) } else { - res = res + fmt.Sprintf(" +%s", c) + res += fmt.Sprintf(" +%s", c) } } resByte := []byte(res) @@ -186,7 +186,7 @@ func createCgroupv2Path(path string) (Err error) { } else { // If the directory was created, be sure it is not left around on errors. defer func() { - if Err != nil { + if deferredError != nil { os.Remove(current) } }() diff --git a/pkg/hooks/1.0.0/when_test.go b/pkg/hooks/1.0.0/when_test.go index a749063ff..94b0c3830 100644 --- a/pkg/hooks/1.0.0/when_test.go +++ b/pkg/hooks/1.0.0/when_test.go @@ -10,7 +10,8 @@ import ( func TestNoMatch(t *testing.T) { config := &rspec.Spec{} - for _, or := range []bool{true, false} { + for _, o := range []bool{true, false} { + or := o t.Run(fmt.Sprintf("or %t", or), func(t *testing.T) { when := When{Or: or} match, err := when.Match(config, map[string]string{}, false) @@ -27,9 +28,12 @@ func TestAlways(t *testing.T) { processStruct := &rspec.Process{ Args: []string{"/bin/sh", "a", "b"}, } - for _, always := range []bool{true, false} { - for _, or := range []bool{true, false} { - for _, process := range []*rspec.Process{processStruct, nil} { + for _, a := range []bool{true, false} { + always := a + for _, o := range []bool{true, false} { + or := o + for _, p := range []*rspec.Process{processStruct, nil} { + process := p t.Run(fmt.Sprintf("always %t, or %t, has process %t", always, or, process != nil), func(t *testing.T) { config.Process = process when := When{Always: &always, Or: or} @@ -48,7 +52,8 @@ func TestHasBindMountsAnd(t *testing.T) { hasBindMounts := true when := When{HasBindMounts: &hasBindMounts} config := &rspec.Spec{} - for _, containerHasBindMounts := range []bool{false, true} { + for _, b := range []bool{false, true} { + containerHasBindMounts := b t.Run(fmt.Sprintf("%t", containerHasBindMounts), func(t *testing.T) { match, err := when.Match(config, map[string]string{}, containerHasBindMounts) if err != nil { @@ -63,7 +68,8 @@ func TestHasBindMountsOr(t *testing.T) { hasBindMounts := true when := When{HasBindMounts: &hasBindMounts, Or: true} config := &rspec.Spec{} - for _, containerHasBindMounts := range []bool{false, true} { + for _, b := range []bool{false, true} { + containerHasBindMounts := b t.Run(fmt.Sprintf("%t", containerHasBindMounts), func(t *testing.T) { match, err := when.Match(config, map[string]string{}, containerHasBindMounts) if err != nil { @@ -82,7 +88,7 @@ func TestAnnotations(t *testing.T) { }, } config := &rspec.Spec{} - for _, test := range []struct { + for _, tt := range []struct { name string annotations map[string]string or bool @@ -131,6 +137,7 @@ func TestAnnotations(t *testing.T) { match: false, }, } { + test := tt t.Run(test.name, func(t *testing.T) { when.Or = test.or match, err := when.Match(config, test.annotations, false) @@ -149,7 +156,7 @@ func TestCommands(t *testing.T) { }, } config := &rspec.Spec{} - for _, test := range []struct { + for _, tt := range []struct { name string process *rspec.Process match bool @@ -173,6 +180,7 @@ func TestCommands(t *testing.T) { match: false, }, } { + test := tt t.Run(test.name, func(t *testing.T) { config.Process = test.process match, err := when.Match(config, map[string]string{}, false) @@ -209,7 +217,7 @@ func TestHasBindMountsAndCommands(t *testing.T) { }, } config := &rspec.Spec{Process: &rspec.Process{}} - for _, test := range []struct { + for _, tt := range []struct { name string command string hasBindMounts bool @@ -273,6 +281,7 @@ func TestHasBindMountsAndCommands(t *testing.T) { match: false, }, } { + test := tt t.Run(test.name, func(t *testing.T) { config.Process.Args = []string{test.command} when.Or = test.or @@ -287,7 +296,7 @@ func TestHasBindMountsAndCommands(t *testing.T) { func TestInvalidRegexp(t *testing.T) { config := &rspec.Spec{Process: &rspec.Process{Args: []string{"/bin/sh"}}} - for _, test := range []struct { + for _, tt := range []struct { name string when When expected string @@ -308,6 +317,7 @@ func TestInvalidRegexp(t *testing.T) { expected: "^command: error parsing regexp: .*", }, } { + test := tt t.Run(test.name, func(t *testing.T) { _, err := test.when.Match(config, map[string]string{"a": "b"}, false) if err == nil { diff --git a/pkg/hooks/docs/oci-hooks.5.md b/pkg/hooks/docs/oci-hooks.5.md index b50a6bddc..7d13ffa82 100644 --- a/pkg/hooks/docs/oci-hooks.5.md +++ b/pkg/hooks/docs/oci-hooks.5.md @@ -25,7 +25,7 @@ Tools consuming this format may also opt to monitor the hook directories for cha Hooks are injected in the order obtained by sorting the JSON file names, after converting them to lower case, based on their Unicode code points. For example, a matching hook defined in `01-my-hook.json` would be injected before matching hooks defined in `02-another-hook.json` and `01-UPPERCASE.json`. -It is strongly recommended to make the sort oder unambiguous depending on an ASCII-only prefix (like the `01`/`02` above). +It is strongly recommended to make the sort order unambiguous depending on an ASCII-only prefix (like the `01`/`02` above). Each JSON file should contain an object with one of the following schemas. diff --git a/pkg/hooks/exec/exec_test.go b/pkg/hooks/exec/exec_test.go index 7aac315cb..1e105373d 100644 --- a/pkg/hooks/exec/exec_test.go +++ b/pkg/hooks/exec/exec_test.go @@ -94,7 +94,7 @@ func TestRunEnvironment(t *testing.T) { Path: path, Args: []string{"sh", "-c", "env"}, } - for _, test := range []struct { + for _, tt := range []struct { name string env []string expected map[string]string @@ -120,6 +120,7 @@ func TestRunEnvironment(t *testing.T) { }, }, } { + test := tt t.Run(test.name, func(t *testing.T) { var stderr, stdout bytes.Buffer hook.Env = test.env @@ -147,7 +148,7 @@ func TestRunCancel(t *testing.T) { Args: []string{"sh", "-c", "echo waiting; sleep 2; echo done"}, } one := 1 - for _, test := range []struct { + for _, tt := range []struct { name string contextTimeout time.Duration hookTimeout *int @@ -174,6 +175,7 @@ func TestRunCancel(t *testing.T) { expectedRunError: context.DeadlineExceeded, }, } { + test := tt t.Run(test.name, func(t *testing.T) { ctx := context.Background() var stderr, stdout bytes.Buffer diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go index 52d590d14..48dd2f998 100644 --- a/pkg/hooks/exec/runtimeconfigfilter_test.go +++ b/pkg/hooks/exec/runtimeconfigfilter_test.go @@ -25,9 +25,9 @@ func pointerFileMode(value os.FileMode) *os.FileMode { } func TestRuntimeConfigFilter(t *testing.T) { - unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) + unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil) //nolint - for _, test := range []struct { + for _, tt := range []struct { name string contextTimeout time.Duration hooks []spec.Hook @@ -244,6 +244,7 @@ func TestRuntimeConfigFilter(t *testing.T) { expectedRunError: unexpectedEndOfJSONInput, }, } { + test := tt t.Run(test.name, func(t *testing.T) { ctx := context.Background() if test.contextTimeout > 0 { diff --git a/pkg/network/network_test.go b/pkg/network/network_test.go index dbffc33ad..1969e792c 100644 --- a/pkg/network/network_test.go +++ b/pkg/network/network_test.go @@ -25,9 +25,10 @@ func Test_networkIntersect(t *testing.T) { {"Two 24s", args{n1: parseCIDR("192.168.1.0/24"), n2: parseCIDR("192.168.2.0/24")}, false}, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { - if got := networkIntersect(tt.args.n1, tt.args.n2); got != tt.want { - t.Errorf("networkIntersect() = %v, want %v", got, tt.want) + if got := networkIntersect(test.args.n1, test.args.n2); got != test.want { + t.Errorf("networkIntersect() = %v, want %v", got, test.want) } }) } diff --git a/pkg/network/subnet_test.go b/pkg/network/subnet_test.go index 6ecfd2d17..917c3be88 100644 --- a/pkg/network/subnet_test.go +++ b/pkg/network/subnet_test.go @@ -20,14 +20,15 @@ func TestNextSubnet(t *testing.T) { {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false}, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := NextSubnet(tt.args.subnet) - if (err != nil) != tt.wantErr { - t.Errorf("NextSubnet() error = %v, wantErr %v", err, tt.wantErr) + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := NextSubnet(test.args.subnet) + if (err != nil) != test.wantErr { + t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NextSubnet() got = %v, want %v", got, tt.want) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("NextSubnet() got = %v, want %v", got, test.want) } }) } diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go index c47156456..ae83c9d52 100644 --- a/pkg/spec/config_linux_cgo.go +++ b/pkg/spec/config_linux_cgo.go @@ -8,13 +8,24 @@ import ( spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" seccomp "github.com/seccomp/containers-golang" + "github.com/sirupsen/logrus" ) func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { var seccompConfig *spec.LinuxSeccomp var err error + if config.SeccompPolicy == SeccompPolicyImage && config.SeccompProfileFromImage != "" { + logrus.Debug("Loading seccomp profile from the security config") + seccompConfig, err = seccomp.LoadProfile(config.SeccompProfileFromImage, configSpec) + if err != nil { + return nil, errors.Wrap(err, "loading seccomp profile failed") + } + return seccompConfig, nil + } + if config.SeccompProfilePath != "" { + logrus.Debugf("Loading seccomp profile from %q", config.SeccompProfilePath) seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) if err != nil { return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath) @@ -24,6 +35,7 @@ func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.Linu return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) } } else { + logrus.Debug("Loading default seccomp profile") seccompConfig, err = seccomp.GetDefaultProfile(configSpec) if err != nil { return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 6d058229b..fb222083b 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -2,6 +2,7 @@ package createconfig import ( "os" + "sort" "strconv" "strings" "syscall" @@ -106,19 +107,63 @@ type NetworkConfig struct { PublishAll bool //publish-all } +// SeccompPolicy determines which seccomp profile gets applied to the container. +type SeccompPolicy int + +const ( + // SeccompPolicyDefault - if set use SecurityConfig.SeccompProfilePath, + // otherwise use the default profile. The SeccompProfilePath might be + // explicitly set by the user. + SeccompPolicyDefault SeccompPolicy = iota + // SeccompPolicyImage - if set use SecurityConfig.SeccompProfileFromImage, + // otherwise follow SeccompPolicyDefault. + SeccompPolicyImage +) + +// Map for easy lookups of supported policies. +var supportedSeccompPolicies = map[string]SeccompPolicy{ + "": SeccompPolicyDefault, + "default": SeccompPolicyDefault, + "image": SeccompPolicyImage, +} + +// LookupSeccompPolicy looksup the corresponding SeccompPolicy for the specified +// string. If none is found, an errors is returned including the list of +// supported policies. +// Note that an empty string resolved to SeccompPolicyDefault. +func LookupSeccompPolicy(s string) (SeccompPolicy, error) { + policy, exists := supportedSeccompPolicies[s] + if exists { + return policy, nil + } + + // Sort the keys first as maps are non-deterministic. + keys := []string{} + for k := range supportedSeccompPolicies { + if k != "" { + keys = append(keys, k) + } + } + sort.Strings(keys) + + return -1, errors.Errorf("invalid seccomp policy %q: valid policies are %+q", s, keys) +} + // SecurityConfig configures the security features for the container type SecurityConfig struct { - CapAdd []string // cap-add - CapDrop []string // cap-drop - LabelOpts []string //SecurityOpts - NoNewPrivs bool //SecurityOpts - ApparmorProfile string //SecurityOpts - SeccompProfilePath string //SecurityOpts - SecurityOpts []string - Privileged bool //privileged - ReadOnlyRootfs bool //read-only - ReadOnlyTmpfs bool //read-only-tmpfs - Sysctl map[string]string //sysctl + CapAdd []string // cap-add + CapDrop []string // cap-drop + LabelOpts []string //SecurityOpts + NoNewPrivs bool //SecurityOpts + ApparmorProfile string //SecurityOpts + SeccompProfilePath string //SecurityOpts + SeccompProfileFromImage string // seccomp profile from the container image + SeccompPolicy SeccompPolicy + SecurityOpts []string + Privileged bool //privileged + ReadOnlyRootfs bool //read-only + ReadOnlyTmpfs bool //read-only-tmpfs + Sysctl map[string]string //sysctl } // CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go index 8e95a3ca0..e62d4ed0a 100644 --- a/pkg/spec/namespaces.go +++ b/pkg/spec/namespaces.go @@ -44,7 +44,8 @@ func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserCon } } - if c.NetMode.IsNS() { + switch { + case c.NetMode.IsNS(): ns := c.NetMode.NS() if ns == "" { return nil, errors.Errorf("invalid empty user-defined network namespace") @@ -53,13 +54,13 @@ func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserCon if err != nil { return nil, err } - } else if c.NetMode.IsContainer() { + case c.NetMode.IsContainer(): connectedCtr, err := runtime.LookupContainer(c.NetMode.Container()) if err != nil { return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container()) } options = append(options, libpod.WithNetNSFrom(connectedCtr)) - } else if !c.NetMode.IsHost() && !c.NetMode.IsNone() { + case !c.NetMode.IsHost() && !c.NetMode.IsNone(): postConfigureNetNS := userns.getPostConfigureNetNS() options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks)) } @@ -102,29 +103,31 @@ func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserCon // state of the NetworkConfig. func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error { netMode := c.NetMode - if netMode.IsHost() { + netCtr := netMode.Container() + switch { + case netMode.IsHost(): logrus.Debug("Using host netmode") if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { return err } - } else if netMode.IsNone() { + case netMode.IsNone(): logrus.Debug("Using none netmode") - } else if netMode.IsBridge() { + case netMode.IsBridge(): logrus.Debug("Using bridge netmode") - } else if netCtr := netMode.Container(); netCtr != "" { + case netCtr != "": logrus.Debugf("using container %s netmode", netCtr) - } else if IsNS(string(netMode)) { + case IsNS(string(netMode)): logrus.Debug("Using ns netmode") if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil { return err } - } else if IsPod(string(netMode)) { + case IsPod(string(netMode)): logrus.Debug("Using pod netmode, unless pod is not sharing") - } else if netMode.IsSlirp4netns() { + case netMode.IsSlirp4netns(): logrus.Debug("Using slirp4netns netmode") - } else if netMode.IsUserDefined() { + case netMode.IsUserDefined(): logrus.Debug("Using user defined netmode") - } else { + default: return errors.Errorf("unknown network mode") } @@ -220,7 +223,8 @@ func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCre // ToCreateOptions converts the input to container create options. func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) { options := make([]libpod.CtrCreateOption, 0) - if c.UsernsMode.IsNS() { + switch { + case c.UsernsMode.IsNS(): ns := c.UsernsMode.NS() if ns == "" { return nil, errors.Errorf("invalid empty user-defined user namespace") @@ -230,13 +234,13 @@ func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreat return nil, err } options = append(options, libpod.WithIDMappings(*c.IDMappings)) - } else if c.UsernsMode.IsContainer() { + case c.UsernsMode.IsContainer(): connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container()) if err != nil { return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container()) } options = append(options, libpod.WithUserNSFrom(connectedCtr)) - } else { + default: options = append(options, libpod.WithIDMappings(*c.IDMappings)) } @@ -413,20 +417,22 @@ func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([ // of the UtsConfig. func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error { hostname := c.Hostname + utsCtrID := c.UtsMode.Container() var err error if hostname == "" { - if utsCtrID := c.UtsMode.Container(); utsCtrID != "" { + switch { + case utsCtrID != "": utsCtr, err := runtime.GetContainer(utsCtrID) if err != nil { return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID) } hostname = utsCtr.Hostname() - } else if net.NetMode.IsHost() || c.UtsMode.IsHost() { + case net.NetMode.IsHost() || c.UtsMode.IsHost(): hostname, err = os.Hostname() if err != nil { return errors.Wrap(err, "unable to retrieve hostname of the host") } - } else { + default: logrus.Debug("No hostname set; container's hostname will default to runtime default") } } diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go index dbdab0030..0e2098c1d 100644 --- a/pkg/spec/storage.go +++ b/pkg/spec/storage.go @@ -409,9 +409,10 @@ func getBindMount(args []string) (spec.Mount, error) { // ro=[true|false] // rw // rw=[true|false] - if len(kv) == 1 { + switch len(kv) { + case 1: newMount.Options = append(newMount.Options, kv[0]) - } else if len(kv) == 2 { + case 2: switch strings.ToLower(kv[1]) { case "true": newMount.Options = append(newMount.Options, kv[0]) @@ -424,7 +425,7 @@ func getBindMount(args []string) (spec.Mount, error) { default: return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1]) } - } else { + default: return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) } case "nosuid", "suid": diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go index e1da7e8e0..3894a0205 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemdgen/systemdgen_test.go @@ -24,9 +24,10 @@ func TestValidateRestartPolicy(t *testing.T) { {"failblank", ContainerInfo{restart: ""}, true}, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { - if err := validateRestartPolicy(tt.ContainerInfo.restart); (err != nil) != tt.wantErr { - t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, tt.wantErr) + if err := validateRestartPolicy(test.ContainerInfo.restart); (err != nil) != test.wantErr { + t.Errorf("ValidateRestartPolicy() error = %v, wantErr %v", err, test.wantErr) } }) } @@ -221,18 +222,19 @@ WantedBy=multi-user.target` }, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { opts := Options{ Files: false, - New: tt.info.New, + New: test.info.New, } - got, err := CreateContainerSystemdUnit(&tt.info, opts) - if (err != nil) != tt.wantErr { - t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, tt.wantErr) + got, err := CreateContainerSystemdUnit(&test.info, opts) + if (err != nil) != test.wantErr { + t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) return } - if got != tt.want { - t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, tt.want) + if got != test.want { + t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) } }) } diff --git a/pkg/timetype/timestamp.go b/pkg/timetype/timestamp.go index eb904a574..2de1a005f 100644 --- a/pkg/timetype/timestamp.go +++ b/pkg/timetype/timestamp.go @@ -34,7 +34,7 @@ func GetTimestamp(value string, reference time.Time) (string, error) { // if the string has a Z or a + or three dashes use parse otherwise use parseinlocation parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3) - if strings.Contains(value, ".") { + if strings.Contains(value, ".") { // nolint(gocritic) if parseInLocation { format = rFC3339NanoLocal } else { diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index d028ddf8f..5be24faaa 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -12,6 +12,7 @@ import ( // Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout. func Init(service string) (opentracing.Tracer, io.Closer) { cfg := &config.Configuration{ + ServiceName: service, Sampler: &config.SamplerConfig{ Type: "const", Param: 1, @@ -20,7 +21,7 @@ func Init(service string) (opentracing.Tracer, io.Closer) { LogSpans: true, }, } - tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger)) + tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger)) if err != nil { panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err)) } diff --git a/pkg/util/utils.go b/pkg/util/utils.go index 9269f6115..6aa3c221e 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -321,14 +321,14 @@ func ParseSignal(rawSignal string) (syscall.Signal, error) { } // ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping -func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) { +func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) { options := storage.IDMappingOptions{ HostUIDMapping: true, HostGIDMapping: true, } if mode.IsKeepID() { - if len(UIDMapSlice) > 0 || len(GIDMapSlice) > 0 { + if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 { return nil, errors.New("cannot specify custom mappings with --userns=keep-id") } if len(subUIDMap) > 0 || len(subGIDMap) > 0 { @@ -384,17 +384,17 @@ func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []strin if subUIDMap == "" && subGIDMap != "" { subUIDMap = subGIDMap } - if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 { - GIDMapSlice = UIDMapSlice + if len(gidMapSlice) == 0 && len(uidMapSlice) != 0 { + gidMapSlice = uidMapSlice } - if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 { - UIDMapSlice = GIDMapSlice + if len(uidMapSlice) == 0 && len(gidMapSlice) != 0 { + uidMapSlice = gidMapSlice } - if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 { - UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())} + if len(uidMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 { + uidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())} } - if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 { - GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())} + if len(gidMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 { + gidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())} } if subUIDMap != "" && subGIDMap != "" { @@ -405,11 +405,11 @@ func ParseIDMapping(mode namespaces.UsernsMode, UIDMapSlice, GIDMapSlice []strin options.UIDMap = mappings.UIDs() options.GIDMap = mappings.GIDs() } - parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID") + parsedUIDMap, err := idtools.ParseIDMap(uidMapSlice, "UID") if err != nil { return nil, err } - parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID") + parsedGIDMap, err := idtools.ParseIDMap(gidMapSlice, "GID") if err != nil { return nil, err } |