mirror of https://github.com/docker/docs.git
Merge pull request #22861 from vdemeester/daemon-images-search-refactoring
Daemon images search refactoring
This commit is contained in:
commit
75109b32db
|
|
@ -234,7 +234,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
//start the container
|
//start the container
|
||||||
if err := cli.client.ContainerStart(ctx, createResponse.ID); err != nil {
|
if err := cli.client.ContainerStart(ctx, createResponse.ID, ""); err != nil {
|
||||||
// If we have holdHijackedConnection, we should notify
|
// If we have holdHijackedConnection, we should notify
|
||||||
// holdHijackedConnection we are going to exit and wait
|
// holdHijackedConnection we are going to exit and wait
|
||||||
// to avoid the terminal are not restored.
|
// to avoid the terminal are not restored.
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
})
|
})
|
||||||
|
|
||||||
// 3. Start the container.
|
// 3. Start the container.
|
||||||
if err := cli.client.ContainerStart(ctx, container); err != nil {
|
if err := cli.client.ContainerStart(ctx, container, ""); err != nil {
|
||||||
cancelFun()
|
cancelFun()
|
||||||
<-cErr
|
<-cErr
|
||||||
return err
|
return err
|
||||||
|
|
@ -147,7 +147,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
||||||
func (cli *DockerCli) startContainersWithoutAttachments(ctx context.Context, containers []string) error {
|
func (cli *DockerCli) startContainersWithoutAttachments(ctx context.Context, containers []string) error {
|
||||||
var failedContainers []string
|
var failedContainers []string
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
if err := cli.client.ContainerStart(ctx, container); err != nil {
|
if err := cli.client.ContainerStart(ctx, container, ""); err != nil {
|
||||||
fmt.Fprintf(cli.err, "%s\n", err)
|
fmt.Fprintf(cli.err, "%s\n", err)
|
||||||
failedContainers = append(failedContainers, container)
|
failedContainers = append(failedContainers, container)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
@ -32,7 +31,6 @@ import (
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
containertypes "github.com/docker/engine-api/types/container"
|
containertypes "github.com/docker/engine-api/types/container"
|
||||||
networktypes "github.com/docker/engine-api/types/network"
|
networktypes "github.com/docker/engine-api/types/network"
|
||||||
registrytypes "github.com/docker/engine-api/types/registry"
|
|
||||||
"github.com/docker/engine-api/types/strslice"
|
"github.com/docker/engine-api/types/strslice"
|
||||||
// register graph drivers
|
// register graph drivers
|
||||||
_ "github.com/docker/docker/daemon/graphdriver/register"
|
_ "github.com/docker/docker/daemon/graphdriver/register"
|
||||||
|
|
@ -60,7 +58,6 @@ import (
|
||||||
volumedrivers "github.com/docker/docker/volume/drivers"
|
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||||
"github.com/docker/docker/volume/local"
|
"github.com/docker/docker/volume/local"
|
||||||
"github.com/docker/docker/volume/store"
|
"github.com/docker/docker/volume/store"
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/docker/libnetwork"
|
"github.com/docker/libnetwork"
|
||||||
nwconfig "github.com/docker/libnetwork/config"
|
nwconfig "github.com/docker/libnetwork/config"
|
||||||
|
|
@ -87,7 +84,7 @@ type Daemon struct {
|
||||||
configStore *Config
|
configStore *Config
|
||||||
statsCollector *statsCollector
|
statsCollector *statsCollector
|
||||||
defaultLogConfig containertypes.LogConfig
|
defaultLogConfig containertypes.LogConfig
|
||||||
RegistryService *registry.Service
|
RegistryService registry.Service
|
||||||
EventsService *events.Events
|
EventsService *events.Events
|
||||||
netController libnetwork.NetworkController
|
netController libnetwork.NetworkController
|
||||||
volumes *store.VolumeStore
|
volumes *store.VolumeStore
|
||||||
|
|
@ -374,7 +371,7 @@ func (daemon *Daemon) registerLink(parent, child *container.Container, alias str
|
||||||
|
|
||||||
// NewDaemon sets up everything for the daemon to be able to service
|
// NewDaemon sets up everything for the daemon to be able to service
|
||||||
// requests from the webserver.
|
// requests from the webserver.
|
||||||
func NewDaemon(config *Config, registryService *registry.Service, containerdRemote libcontainerd.Remote) (daemon *Daemon, err error) {
|
func NewDaemon(config *Config, registryService registry.Service, containerdRemote libcontainerd.Remote) (daemon *Daemon, err error) {
|
||||||
setDefaultMtu(config)
|
setDefaultMtu(config)
|
||||||
|
|
||||||
// Ensure we have compatible and valid configuration options
|
// Ensure we have compatible and valid configuration options
|
||||||
|
|
@ -888,88 +885,7 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore,
|
||||||
|
|
||||||
// AuthenticateToRegistry checks the validity of credentials in authConfig
|
// AuthenticateToRegistry checks the validity of credentials in authConfig
|
||||||
func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) {
|
func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) {
|
||||||
return daemon.RegistryService.Auth(authConfig, dockerversion.DockerUserAgent(ctx))
|
return daemon.RegistryService.Auth(ctx, authConfig, dockerversion.DockerUserAgent(ctx))
|
||||||
}
|
|
||||||
|
|
||||||
var acceptedSearchFilterTags = map[string]bool{
|
|
||||||
"is-automated": true,
|
|
||||||
"is-official": true,
|
|
||||||
"stars": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchRegistryForImages queries the registry for images matching
|
|
||||||
// term. authConfig is used to login.
|
|
||||||
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
|
|
||||||
authConfig *types.AuthConfig,
|
|
||||||
headers map[string][]string) (*registrytypes.SearchResults, error) {
|
|
||||||
|
|
||||||
searchFilters, err := filters.FromParam(filtersArgs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
unfilteredResult, err := daemon.RegistryService.Search(term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var isAutomated, isOfficial bool
|
|
||||||
var hasStarFilter = 0
|
|
||||||
if searchFilters.Include("is-automated") {
|
|
||||||
if searchFilters.ExactMatch("is-automated", "true") {
|
|
||||||
isAutomated = true
|
|
||||||
} else if !searchFilters.ExactMatch("is-automated", "false") {
|
|
||||||
return nil, fmt.Errorf("Invalid filter 'is-automated=%s'", searchFilters.Get("is-automated"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if searchFilters.Include("is-official") {
|
|
||||||
if searchFilters.ExactMatch("is-official", "true") {
|
|
||||||
isOfficial = true
|
|
||||||
} else if !searchFilters.ExactMatch("is-official", "false") {
|
|
||||||
return nil, fmt.Errorf("Invalid filter 'is-official=%s'", searchFilters.Get("is-official"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if searchFilters.Include("stars") {
|
|
||||||
hasStars := searchFilters.Get("stars")
|
|
||||||
for _, hasStar := range hasStars {
|
|
||||||
iHasStar, err := strconv.Atoi(hasStar)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Invalid filter 'stars=%s'", hasStar)
|
|
||||||
}
|
|
||||||
if iHasStar > hasStarFilter {
|
|
||||||
hasStarFilter = iHasStar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredResults := []registrytypes.SearchResult{}
|
|
||||||
for _, result := range unfilteredResult.Results {
|
|
||||||
if searchFilters.Include("is-automated") {
|
|
||||||
if isAutomated != result.IsAutomated {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if searchFilters.Include("is-official") {
|
|
||||||
if isOfficial != result.IsOfficial {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if searchFilters.Include("stars") {
|
|
||||||
if result.StarCount < hasStarFilter {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filteredResults = append(filteredResults, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ®istrytypes.SearchResults{
|
|
||||||
Query: unfilteredResult.Query,
|
|
||||||
NumResults: len(filteredResults),
|
|
||||||
Results: filteredResults,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsShuttingDown tells whether the daemon is shutting down or not
|
// IsShuttingDown tells whether the daemon is shutting down or not
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/dockerversion"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"github.com/docker/engine-api/types/filters"
|
||||||
|
registrytypes "github.com/docker/engine-api/types/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var acceptedSearchFilterTags = map[string]bool{
|
||||||
|
"is-automated": true,
|
||||||
|
"is-official": true,
|
||||||
|
"stars": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRegistryForImages queries the registry for images matching
|
||||||
|
// term. authConfig is used to login.
|
||||||
|
func (daemon *Daemon) SearchRegistryForImages(ctx context.Context, filtersArgs string, term string,
|
||||||
|
authConfig *types.AuthConfig,
|
||||||
|
headers map[string][]string) (*registrytypes.SearchResults, error) {
|
||||||
|
|
||||||
|
searchFilters, err := filters.FromParam(filtersArgs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := searchFilters.Validate(acceptedSearchFilterTags); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
unfilteredResult, err := daemon.RegistryService.Search(ctx, term, authConfig, dockerversion.DockerUserAgent(ctx), headers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var isAutomated, isOfficial bool
|
||||||
|
var hasStarFilter = 0
|
||||||
|
if searchFilters.Include("is-automated") {
|
||||||
|
if searchFilters.UniqueExactMatch("is-automated", "true") {
|
||||||
|
isAutomated = true
|
||||||
|
} else if !searchFilters.UniqueExactMatch("is-automated", "false") {
|
||||||
|
return nil, fmt.Errorf("Invalid filter 'is-automated=%s'", searchFilters.Get("is-automated"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if searchFilters.Include("is-official") {
|
||||||
|
if searchFilters.UniqueExactMatch("is-official", "true") {
|
||||||
|
isOfficial = true
|
||||||
|
} else if !searchFilters.UniqueExactMatch("is-official", "false") {
|
||||||
|
return nil, fmt.Errorf("Invalid filter 'is-official=%s'", searchFilters.Get("is-official"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if searchFilters.Include("stars") {
|
||||||
|
hasStars := searchFilters.Get("stars")
|
||||||
|
for _, hasStar := range hasStars {
|
||||||
|
iHasStar, err := strconv.Atoi(hasStar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Invalid filter 'stars=%s'", hasStar)
|
||||||
|
}
|
||||||
|
if iHasStar > hasStarFilter {
|
||||||
|
hasStarFilter = iHasStar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredResults := []registrytypes.SearchResult{}
|
||||||
|
for _, result := range unfilteredResult.Results {
|
||||||
|
if searchFilters.Include("is-automated") {
|
||||||
|
if isAutomated != result.IsAutomated {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if searchFilters.Include("is-official") {
|
||||||
|
if isOfficial != result.IsOfficial {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if searchFilters.Include("stars") {
|
||||||
|
if result.StarCount < hasStarFilter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filteredResults = append(filteredResults, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ®istrytypes.SearchResults{
|
||||||
|
Query: unfilteredResult.Query,
|
||||||
|
NumResults: len(filteredResults),
|
||||||
|
Results: filteredResults,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,357 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"github.com/docker/docker/registry"
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
registrytypes "github.com/docker/engine-api/types/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeService struct {
|
||||||
|
registry.DefaultService
|
||||||
|
|
||||||
|
shouldReturnError bool
|
||||||
|
|
||||||
|
term string
|
||||||
|
results []registrytypes.SearchResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FakeService) Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
|
||||||
|
if s.shouldReturnError {
|
||||||
|
return nil, fmt.Errorf("Search unknown error")
|
||||||
|
}
|
||||||
|
return ®istrytypes.SearchResults{
|
||||||
|
Query: s.term,
|
||||||
|
NumResults: len(s.results),
|
||||||
|
Results: s.results,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchRegistryForImagesErrors(t *testing.T) {
|
||||||
|
errorCases := []struct {
|
||||||
|
filtersArgs string
|
||||||
|
shouldReturnError bool
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
expectedError: "Search unknown error",
|
||||||
|
shouldReturnError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: "invalid json",
|
||||||
|
expectedError: "invalid character 'i' looking for beginning of value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"type":{"custom":true}}`,
|
||||||
|
expectedError: "Invalid filter 'type'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"invalid":true}}`,
|
||||||
|
expectedError: "Invalid filter 'is-automated=[invalid]'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"true":true,"false":true}}`,
|
||||||
|
expectedError: "Invalid filter 'is-automated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"invalid":true}}`,
|
||||||
|
expectedError: "Invalid filter 'is-official=[invalid]'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"true":true,"false":true}}`,
|
||||||
|
expectedError: "Invalid filter 'is-official",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"invalid":true}}`,
|
||||||
|
expectedError: "Invalid filter 'stars=invalid'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"1":true,"invalid":true}}`,
|
||||||
|
expectedError: "Invalid filter 'stars=invalid'",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for index, e := range errorCases {
|
||||||
|
daemon := &Daemon{
|
||||||
|
RegistryService: &FakeService{
|
||||||
|
shouldReturnError: e.shouldReturnError,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := daemon.SearchRegistryForImages(context.Background(), e.filtersArgs, "term", nil, map[string][]string{})
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%d: expected an error, got nothing", index)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), e.expectedError) {
|
||||||
|
t.Errorf("%d: expected error to contain %s, got %s", index, e.expectedError, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSearchRegistryForImages(t *testing.T) {
|
||||||
|
term := "term"
|
||||||
|
successCases := []struct {
|
||||||
|
filtersArgs string
|
||||||
|
registryResults []registrytypes.SearchResult
|
||||||
|
expectedResults []registrytypes.SearchResult
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
filtersArgs: "",
|
||||||
|
registryResults: []registrytypes.SearchResult{},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: "",
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"true":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"true":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"false":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-automated":{"false":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsAutomated: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsAutomated: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"true":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"true":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsOfficial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsOfficial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"false":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsOfficial: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"is-official":{"false":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsOfficial: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
IsOfficial: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"0":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
StarCount: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
StarCount: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"1":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name",
|
||||||
|
Description: "description",
|
||||||
|
StarCount: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"1":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name0",
|
||||||
|
Description: "description0",
|
||||||
|
StarCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "name1",
|
||||||
|
Description: "description1",
|
||||||
|
StarCount: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name1",
|
||||||
|
Description: "description1",
|
||||||
|
StarCount: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filtersArgs: `{"stars":{"1":true}, "is-official":{"true":true}, "is-automated":{"true":true}}`,
|
||||||
|
registryResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name0",
|
||||||
|
Description: "description0",
|
||||||
|
StarCount: 0,
|
||||||
|
IsOfficial: true,
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "name1",
|
||||||
|
Description: "description1",
|
||||||
|
StarCount: 1,
|
||||||
|
IsOfficial: false,
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "name2",
|
||||||
|
Description: "description2",
|
||||||
|
StarCount: 1,
|
||||||
|
IsOfficial: true,
|
||||||
|
IsAutomated: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "name3",
|
||||||
|
Description: "description3",
|
||||||
|
StarCount: 2,
|
||||||
|
IsOfficial: true,
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResults: []registrytypes.SearchResult{
|
||||||
|
{
|
||||||
|
Name: "name3",
|
||||||
|
Description: "description3",
|
||||||
|
StarCount: 2,
|
||||||
|
IsOfficial: true,
|
||||||
|
IsAutomated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for index, s := range successCases {
|
||||||
|
daemon := &Daemon{
|
||||||
|
RegistryService: &FakeService{
|
||||||
|
term: term,
|
||||||
|
results: s.registryResults,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
results, err := daemon.SearchRegistryForImages(context.Background(), s.filtersArgs, term, nil, map[string][]string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", index, err)
|
||||||
|
}
|
||||||
|
if results.Query != term {
|
||||||
|
t.Errorf("%d: expected Query to be %s, got %s", index, term, results.Query)
|
||||||
|
}
|
||||||
|
if results.NumResults != len(s.expectedResults) {
|
||||||
|
t.Errorf("%d: expected NumResults to be %d, got %d", index, len(s.expectedResults), results.NumResults)
|
||||||
|
}
|
||||||
|
for _, result := range results.Results {
|
||||||
|
found := false
|
||||||
|
for _, expectedResult := range s.expectedResults {
|
||||||
|
if expectedResult.Name == result.Name &&
|
||||||
|
expectedResult.Description == result.Description &&
|
||||||
|
expectedResult.IsAutomated == result.IsAutomated &&
|
||||||
|
expectedResult.IsOfficial == result.IsOfficial &&
|
||||||
|
expectedResult.StarCount == result.StarCount {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Errorf("%d: expected results %v, got %v", index, s.expectedResults, results.Results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ type ImagePullConfig struct {
|
||||||
ProgressOutput progress.Output
|
ProgressOutput progress.Output
|
||||||
// RegistryService is the registry service to use for TLS configuration
|
// RegistryService is the registry service to use for TLS configuration
|
||||||
// and endpoint lookup.
|
// and endpoint lookup.
|
||||||
RegistryService *registry.Service
|
RegistryService registry.Service
|
||||||
// ImageEventLogger notifies events for a given image
|
// ImageEventLogger notifies events for a given image
|
||||||
ImageEventLogger func(id, name, action string)
|
ImageEventLogger func(id, name, action string)
|
||||||
// MetadataStore is the storage backend for distribution-specific
|
// MetadataStore is the storage backend for distribution-specific
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ type ImagePushConfig struct {
|
||||||
ProgressOutput progress.Output
|
ProgressOutput progress.Output
|
||||||
// RegistryService is the registry service to use for TLS configuration
|
// RegistryService is the registry service to use for TLS configuration
|
||||||
// and endpoint lookup.
|
// and endpoint lookup.
|
||||||
RegistryService *registry.Service
|
RegistryService registry.Service
|
||||||
// ImageEventLogger notifies events for a given image
|
// ImageEventLogger notifies events for a given image
|
||||||
ImageEventLogger func(id, name, action string)
|
ImageEventLogger func(id, name, action string)
|
||||||
// MetadataStore is the storage backend for distribution-specific
|
// MetadataStore is the storage backend for distribution-specific
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ clone git golang.org/x/net 78cb2c067747f08b343f20614155233ab4ea2ad3 https://gith
|
||||||
clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
|
clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
|
||||||
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
|
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
|
||||||
clone git github.com/docker/go-connections v0.2.0
|
clone git github.com/docker/go-connections v0.2.0
|
||||||
clone git github.com/docker/engine-api e374c4fb5b121a8fd4295ec5eb91a8068c6304f4
|
clone git github.com/docker/engine-api 12fbeb3ac3ca5dc5d0f01d6bac9bda518d46d983
|
||||||
clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
|
clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
|
||||||
clone git github.com/imdario/mergo 0.2.1
|
clone git github.com/imdario/mergo 0.2.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -661,7 +661,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
s := Service{config: makeServiceConfig([]string{"my.mirror"}, nil)}
|
s := DefaultService{config: makeServiceConfig([]string{"my.mirror"}, nil)}
|
||||||
|
|
||||||
imageName, err := reference.WithName(IndexName + "/test/image")
|
imageName, err := reference.WithName(IndexName + "/test/image")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -7,35 +7,50 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/reference"
|
"github.com/docker/docker/reference"
|
||||||
"github.com/docker/engine-api/types"
|
"github.com/docker/engine-api/types"
|
||||||
registrytypes "github.com/docker/engine-api/types/registry"
|
registrytypes "github.com/docker/engine-api/types/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service is a registry service. It tracks configuration data such as a list
|
// Service is the interface defining what a registry service should implement.
|
||||||
|
type Service interface {
|
||||||
|
Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error)
|
||||||
|
LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error)
|
||||||
|
LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error)
|
||||||
|
ResolveRepository(name reference.Named) (*RepositoryInfo, error)
|
||||||
|
ResolveIndex(name string) (*registrytypes.IndexInfo, error)
|
||||||
|
Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error)
|
||||||
|
ServiceConfig() *registrytypes.ServiceConfig
|
||||||
|
TLSConfig(hostname string) (*tls.Config, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultService is a registry service. It tracks configuration data such as a list
|
||||||
// of mirrors.
|
// of mirrors.
|
||||||
type Service struct {
|
type DefaultService struct {
|
||||||
config *serviceConfig
|
config *serviceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns a new instance of Service ready to be
|
// NewService returns a new instance of DefaultService ready to be
|
||||||
// installed into an engine.
|
// installed into an engine.
|
||||||
func NewService(options ServiceOptions) *Service {
|
func NewService(options ServiceOptions) *DefaultService {
|
||||||
return &Service{
|
return &DefaultService{
|
||||||
config: newServiceConfig(options),
|
config: newServiceConfig(options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceConfig returns the public registry service configuration.
|
// ServiceConfig returns the public registry service configuration.
|
||||||
func (s *Service) ServiceConfig() *registrytypes.ServiceConfig {
|
func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
||||||
return &s.config.ServiceConfig
|
return &s.config.ServiceConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth contacts the public registry with the provided credentials,
|
// Auth contacts the public registry with the provided credentials,
|
||||||
// and returns OK if authentication was successful.
|
// and returns OK if authentication was successful.
|
||||||
// It can be used to verify the validity of a client's credentials.
|
// It can be used to verify the validity of a client's credentials.
|
||||||
func (s *Service) Auth(authConfig *types.AuthConfig, userAgent string) (status, token string, err error) {
|
func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error) {
|
||||||
|
// TODO Use ctx when searching for repositories
|
||||||
serverAddress := authConfig.ServerAddress
|
serverAddress := authConfig.ServerAddress
|
||||||
if serverAddress == "" {
|
if serverAddress == "" {
|
||||||
serverAddress = IndexServer
|
serverAddress = IndexServer
|
||||||
|
|
@ -93,7 +108,8 @@ func splitReposSearchTerm(reposName string) (string, string) {
|
||||||
|
|
||||||
// Search queries the public registry for images matching the specified
|
// Search queries the public registry for images matching the specified
|
||||||
// search terms, and returns the results.
|
// search terms, and returns the results.
|
||||||
func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
|
func (s *DefaultService) Search(ctx context.Context, term string, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) {
|
||||||
|
// TODO Use ctx when searching for repositories
|
||||||
if err := validateNoScheme(term); err != nil {
|
if err := validateNoScheme(term); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -130,12 +146,12 @@ func (s *Service) Search(term string, authConfig *types.AuthConfig, userAgent st
|
||||||
|
|
||||||
// ResolveRepository splits a repository name into its components
|
// ResolveRepository splits a repository name into its components
|
||||||
// and configuration of the associated registry.
|
// and configuration of the associated registry.
|
||||||
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
func (s *DefaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
||||||
return newRepositoryInfo(s.config, name)
|
return newRepositoryInfo(s.config, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveIndex takes indexName and returns index info
|
// ResolveIndex takes indexName and returns index info
|
||||||
func (s *Service) ResolveIndex(name string) (*registrytypes.IndexInfo, error) {
|
func (s *DefaultService) ResolveIndex(name string) (*registrytypes.IndexInfo, error) {
|
||||||
return newIndexInfo(s.config, name)
|
return newIndexInfo(s.config, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,25 +171,25 @@ func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) (*V
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfig constructs a client TLS configuration based on server defaults
|
// TLSConfig constructs a client TLS configuration based on server defaults
|
||||||
func (s *Service) TLSConfig(hostname string) (*tls.Config, error) {
|
func (s *DefaultService) TLSConfig(hostname string) (*tls.Config, error) {
|
||||||
return newTLSConfig(hostname, isSecureIndex(s.config, hostname))
|
return newTLSConfig(hostname, isSecureIndex(s.config, hostname))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) tlsConfigForMirror(mirrorURL *url.URL) (*tls.Config, error) {
|
func (s *DefaultService) tlsConfigForMirror(mirrorURL *url.URL) (*tls.Config, error) {
|
||||||
return s.TLSConfig(mirrorURL.Host)
|
return s.TLSConfig(mirrorURL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPullEndpoints creates a list of endpoints to try to pull from, in order of preference.
|
// LookupPullEndpoints creates a list of endpoints to try to pull from, in order of preference.
|
||||||
// It gives preference to v2 endpoints over v1, mirrors over the actual
|
// It gives preference to v2 endpoints over v1, mirrors over the actual
|
||||||
// registry, and HTTPS over plain HTTP.
|
// registry, and HTTPS over plain HTTP.
|
||||||
func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
func (s *DefaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||||
return s.lookupEndpoints(hostname)
|
return s.lookupEndpoints(hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupPushEndpoints creates a list of endpoints to try to push to, in order of preference.
|
// LookupPushEndpoints creates a list of endpoints to try to push to, in order of preference.
|
||||||
// It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
|
// It gives preference to v2 endpoints over v1, and HTTPS over plain HTTP.
|
||||||
// Mirrors are not included.
|
// Mirrors are not included.
|
||||||
func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||||
allEndpoints, err := s.lookupEndpoints(hostname)
|
allEndpoints, err := s.lookupEndpoints(hostname)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, endpoint := range allEndpoints {
|
for _, endpoint := range allEndpoints {
|
||||||
|
|
@ -185,7 +201,7 @@ func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint,
|
||||||
return endpoints, err
|
return endpoints, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
func (s *DefaultService) lookupEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||||
endpoints, err = s.lookupV2Endpoints(hostname)
|
endpoints, err = s.lookupV2Endpoints(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/docker/go-connections/tlsconfig"
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) lookupV1Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
func (s *DefaultService) lookupV1Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||||
var cfg = tlsconfig.ServerDefault
|
var cfg = tlsconfig.ServerDefault
|
||||||
tlsConfig := &cfg
|
tlsConfig := &cfg
|
||||||
if hostname == DefaultNamespace {
|
if hostname == DefaultNamespace {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/docker/go-connections/tlsconfig"
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||||
var cfg = tlsconfig.ServerDefault
|
var cfg = tlsconfig.ServerDefault
|
||||||
tlsConfig := &cfg
|
tlsConfig := &cfg
|
||||||
if hostname == DefaultNamespace || hostname == DefaultV1Registry.Host {
|
if hostname == DefaultNamespace || hostname == DefaultV1Registry.Host {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointCreate creates a checkpoint from the given container with the given name
|
||||||
|
func (cli *Client) CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error {
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+container+"/checkpoints", nil, options, nil)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointDelete deletes the checkpoint with the given name from the given container
|
||||||
|
func (cli *Client) CheckpointDelete(ctx context.Context, containerID string, checkpointID string) error {
|
||||||
|
resp, err := cli.delete(ctx, "/containers/"+containerID+"/checkpoints/"+checkpointID, nil, nil)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/docker/engine-api/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckpointList returns the volumes configured in the docker host.
|
||||||
|
func (cli *Client) CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error) {
|
||||||
|
var checkpoints []types.Checkpoint
|
||||||
|
|
||||||
|
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
||||||
|
ensureReaderClosed(resp)
|
||||||
|
return checkpoints, err
|
||||||
|
}
|
||||||
|
|
@ -52,14 +52,3 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
|
||||||
err = json.NewDecoder(rdr).Decode(&response)
|
err = json.NewDecoder(rdr).Decode(&response)
|
||||||
return response, body, err
|
return response, body, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *Client) containerInspectWithResponse(ctx context.Context, containerID string, query url.Values) (types.ContainerJSON, *serverResponse, error) {
|
|
||||||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.ContainerJSON{}, serverResp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response types.ContainerJSON
|
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&response)
|
|
||||||
return response, serverResp, err
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import "golang.org/x/net/context"
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
// ContainerStart sends a request to the docker daemon to start a container.
|
// ContainerStart sends a request to the docker daemon to start a container.
|
||||||
func (cli *Client) ContainerStart(ctx context.Context, containerID string) error {
|
func (cli *Client) ContainerStart(ctx context.Context, containerID string, checkpointID string) error {
|
||||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", nil, nil, nil)
|
query := url.Values{}
|
||||||
|
query.Set("checkpoint", checkpointID)
|
||||||
|
|
||||||
|
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
|
||||||
ensureReaderClosed(resp)
|
ensureReaderClosed(resp)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.
|
||||||
query.Set("until", ts)
|
query.Set("until", ts)
|
||||||
}
|
}
|
||||||
if options.Filters.Len() > 0 {
|
if options.Filters.Len() > 0 {
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
if options.Filters.Len() > 0 {
|
if options.Filters.Len() > 0 {
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return images, err
|
return images, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ import (
|
||||||
// APIClient is an interface that clients that talk with a docker server must implement.
|
// APIClient is an interface that clients that talk with a docker server must implement.
|
||||||
type APIClient interface {
|
type APIClient interface {
|
||||||
ClientVersion() string
|
ClientVersion() string
|
||||||
|
CheckpointCreate(ctx context.Context, container string, options types.CheckpointCreateOptions) error
|
||||||
|
CheckpointDelete(ctx context.Context, container string, checkpointID string) error
|
||||||
|
CheckpointList(ctx context.Context, container string) ([]types.Checkpoint, error)
|
||||||
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
ContainerAttach(ctx context.Context, container string, options types.ContainerAttachOptions) (types.HijackedResponse, error)
|
||||||
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
|
ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.ContainerCommitResponse, error)
|
||||||
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
|
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (types.ContainerCreateResponse, error)
|
||||||
|
|
@ -37,7 +40,7 @@ type APIClient interface {
|
||||||
ContainerRestart(ctx context.Context, container string, timeout int) error
|
ContainerRestart(ctx context.Context, container string, timeout int) error
|
||||||
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
|
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
|
||||||
ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
|
ContainerStats(ctx context.Context, container string, stream bool) (io.ReadCloser, error)
|
||||||
ContainerStart(ctx context.Context, container string) error
|
ContainerStart(ctx context.Context, container string, checkpointID string) error
|
||||||
ContainerStop(ctx context.Context, container string, timeout int) error
|
ContainerStop(ctx context.Context, container string, timeout int) error
|
||||||
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
|
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, error)
|
||||||
ContainerUnpause(ctx context.Context, container string) error
|
ContainerUnpause(ctx context.Context, container string) error
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
func (cli *Client) NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.Filters.Len() > 0 {
|
if options.Filters.Len() > 0 {
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,8 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
|
||||||
|
|
||||||
func ensureReaderClosed(response *serverResponse) {
|
func ensureReaderClosed(response *serverResponse) {
|
||||||
if response != nil && response.body != nil {
|
if response != nil && response.body != nil {
|
||||||
|
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
|
||||||
|
io.CopyN(ioutil.Discard, response.body, 512)
|
||||||
response.body.Close()
|
response.body.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (types.V
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
if filter.Len() > 0 {
|
if filter.Len() > 0 {
|
||||||
filterJSON, err := filters.ToParam(filter)
|
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return volumes, err
|
return volumes, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ import (
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||||
|
type CheckpointCreateOptions struct {
|
||||||
|
CheckpointID string
|
||||||
|
Exit bool
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerAttachOptions holds parameters to attach to a container.
|
// ContainerAttachOptions holds parameters to attach to a container.
|
||||||
type ContainerAttachOptions struct {
|
type ContainerAttachOptions struct {
|
||||||
Stream bool
|
Stream bool
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,6 @@ type Resources struct {
|
||||||
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||||
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||||
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||||
NetworkMaximumBandwidth uint64 // Maximum bandwidth of the network endpoint in bytes per second
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateConfig holds the mutable attributes of a Container.
|
// UpdateConfig holds the mutable attributes of a Container.
|
||||||
|
|
|
||||||
|
|
@ -215,12 +215,24 @@ func (filters Args) ExactMatch(field, source string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to match full name value to avoid O(N) regular expression matching
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
if fieldValues[source] {
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniqueExactMatch returns true if there is only one filter and the source matches exactly this one.
|
||||||
|
func (filters Args) UniqueExactMatch(field, source string) bool {
|
||||||
|
fieldValues := filters.fields[field]
|
||||||
|
//do not filter if there is no filter set or cannot determine filter
|
||||||
|
if len(fieldValues) == 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if len(filters.fields[field]) != 1 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try to match full name value to avoid O(N) regular expression matching
|
||||||
|
return fieldValues[source]
|
||||||
|
}
|
||||||
|
|
||||||
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
||||||
// or the source has one of the filters as a prefix.
|
// or the source has one of the filters as a prefix.
|
||||||
func (filters Args) FuzzyMatch(field, source string) bool {
|
func (filters Args) FuzzyMatch(field, source string) bool {
|
||||||
|
|
|
||||||
|
|
@ -471,3 +471,8 @@ type NetworkDisconnect struct {
|
||||||
Container string
|
Container string
|
||||||
Force bool
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checkpoint represents the details of a checkpoint
|
||||||
|
type Checkpoint struct {
|
||||||
|
Name string // Name is the name of the checkpoint
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue