container/exec: Support ConsoleSize
Relates to 
- https://github.com/docker/cli/issues/3554

Now client have the possibility to set the console size of the executed process immediately at the creation. This makes a difference for example when executing commands that output some kind of text user interface which is bounded by the console dimensions.

**- What I did**
Make exec accept the console size and set it for the created exec

**- How I did it**
Introduced ConsoleSize parameter which is copied to specs.Process that is passed to containerd.Exec

**- How to verify it**
- Run the TestExecConsoleSize integration test.
- With CLI changes you can also check with `docker exec`:
```
# resize terminal from the default size

cid=$(docker run -d ubuntu sleep infinity)

docker exec -it $cid stty size
# check if the printed size is as expected

docker exec -it $cid top -n 1
# check if top UI is rendered at the full terminal size
```

**- Description for the changelog**
Exec allows to set the console size for the exec'd process immediately when it's created


**- A picture of a cute animal (not mandatory but encouraged)**
![dudus2](https://user-images.githubusercontent.com/5046555/169486224-a3b12c1c-0878-4ca2-afde-535c7a22a202.jpg)


client/container_exec.go
package client // import "github.com/docker/docker/client"

import (
	"context"
	"encoding/json"

	"github.com/docker/docker/api/types"
)

// ContainerExecCreate creates a new exec configuration to run an exec process.
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) {
	var response types.IDResponse

	if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil {
		return response, err
	}

	resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
	defer ensureReaderClosed(resp)
	if err != nil {
		return response, err
	}
	err = json.NewDecoder(resp.body).Decode(&response)
	return response, err
}

// ContainerExecStart starts an exec process already created in the docker host.
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
	resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil)
	ensureReaderClosed(resp)
	return err
}

// ContainerExecAttach attaches a connection to an exec process in the server.
// It returns a types.HijackedConnection with the hijacked connection
// and the a reader to get output. It's up to the called to close
// the hijacked connection by calling types.HijackedResponse.Close.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) {
	headers := map[string][]string{
		"Content-Type": {"application/json"},
	}
	return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers)
}

// ContainerExecInspect returns information about a specific exec process on the docker host.
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error) {
	var response types.ContainerExecInspect
	resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil)
	if err != nil {
		return response, err
	}

	err = json.NewDecoder(resp.body).Decode(&response)
	ensureReaderClosed(resp)
	return response, err
}

package client // import "github.com/docker/docker/client"

import (
	"context"
	"encoding/json"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/versions"
)

// ContainerExecCreate creates a new exec configuration to run an exec process.
func (cli *Client) ContainerExecCreate(ctx context.Context, container string, config types.ExecConfig) (types.IDResponse, error) {
	var response types.IDResponse

	if err := cli.NewVersionError("1.25", "env"); len(config.Env) != 0 && err != nil {
		return response, err
	}
	if versions.LessThan(cli.ClientVersion(), "1.42") {
		config.ConsoleSize = nil
	}

	resp, err := cli.post(ctx, "/containers/"+container+"/exec", nil, config, nil)
	defer ensureReaderClosed(resp)
	if err != nil {
		return response, err
	}
	err = json.NewDecoder(resp.body).Decode(&response)
	return response, err
}

// ContainerExecStart starts an exec process already created in the docker host.
func (cli *Client) ContainerExecStart(ctx context.Context, execID string, config types.ExecStartCheck) error {
	if versions.LessThan(cli.ClientVersion(), "1.42") {
		config.ConsoleSize = nil
	}
	resp, err := cli.post(ctx, "/exec/"+execID+"/start", nil, config, nil)
	ensureReaderClosed(resp)
	return err
}

// ContainerExecAttach attaches a connection to an exec process in the server.
// It returns a types.HijackedConnection with the hijacked connection
// and the a reader to get output. It's up to the called to close
// the hijacked connection by calling types.HijackedResponse.Close.
func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) {
	if versions.LessThan(cli.ClientVersion(), "1.42") {
		config.ConsoleSize = nil
	}
	headers := map[string][]string{
		"Content-Type": {"application/json"},
	}
	return cli.postHijacked(ctx, "/exec/"+execID+"/start", nil, config, headers)
}

// ContainerExecInspect returns information about a specific exec process on the docker host.
func (cli *Client) ContainerExecInspect(ctx context.Context, execID string) (types.ContainerExecInspect, error) {
	var response types.ContainerExecInspect
	resp, err := cli.get(ctx, "/exec/"+execID+"/json", nil, nil)
	if err != nil {
		return response, err
	}

	err = json.NewDecoder(resp.body).Decode(&response)
	ensureReaderClosed(resp)
	return response, err
}

Perhaps for this one we shouldn't produce an error (we are a bit inconsistent on this though!); my train of thought here is that; unlike `env` above, the console size is not an explicit option set by the user; this option is a bit similar to how we moved the "auto remove"  (`--rm`) from the client to the daemon; https://github.com/moby/moby/blob/b6dab55339382e2b6df0678a511a75c146daa45b/client/container_create.go#L32-L35

Having this produce an error means that we also need to check the API version in `docker/cli`. Perhaps instead we could just set it unconditionally in the CLI (and have the client discard the value), which could be slightly easier to use.

Happy to hear your (and other's) thoughts on that as well though!