mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #22861 from vdemeester/daemon-images-search-refactoring
Daemon images search refactoring
This commit is contained in:
commit
75109b32db
27 changed files with 590 additions and 137 deletions
|
@ -234,7 +234,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
}
|
||||
|
||||
//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
|
||||
// holdHijackedConnection we are going to exit and wait
|
||||
// to avoid the terminal are not restored.
|
||||
|
|
|
@ -113,7 +113,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
})
|
||||
|
||||
// 3. Start the container.
|
||||
if err := cli.client.ContainerStart(ctx, container); err != nil {
|
||||
if err := cli.client.ContainerStart(ctx, container, ""); err != nil {
|
||||
cancelFun()
|
||||
<-cErr
|
||||
return err
|
||||
|
@ -147,7 +147,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
|
|||
func (cli *DockerCli) startContainersWithoutAttachments(ctx context.Context, containers []string) error {
|
||||
var failedContainers []string
|
||||
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)
|
||||
failedContainers = append(failedContainers, container)
|
||||
} else {
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
@ -32,7 +31,6 @@ import (
|
|||
"github.com/docker/engine-api/types"
|
||||
containertypes "github.com/docker/engine-api/types/container"
|
||||
networktypes "github.com/docker/engine-api/types/network"
|
||||
registrytypes "github.com/docker/engine-api/types/registry"
|
||||
"github.com/docker/engine-api/types/strslice"
|
||||
// register graph drivers
|
||||
_ "github.com/docker/docker/daemon/graphdriver/register"
|
||||
|
@ -60,7 +58,6 @@ import (
|
|||
volumedrivers "github.com/docker/docker/volume/drivers"
|
||||
"github.com/docker/docker/volume/local"
|
||||
"github.com/docker/docker/volume/store"
|
||||
"github.com/docker/engine-api/types/filters"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
|
@ -87,7 +84,7 @@ type Daemon struct {
|
|||
configStore *Config
|
||||
statsCollector *statsCollector
|
||||
defaultLogConfig containertypes.LogConfig
|
||||
RegistryService *registry.Service
|
||||
RegistryService registry.Service
|
||||
EventsService *events.Events
|
||||
netController libnetwork.NetworkController
|
||||
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
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
func (daemon *Daemon) AuthenticateToRegistry(ctx context.Context, authConfig *types.AuthConfig) (string, string, error) {
|
||||
return daemon.RegistryService.Auth(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
|
||||
return daemon.RegistryService.Auth(ctx, authConfig, dockerversion.DockerUserAgent(ctx))
|
||||
}
|
||||
|
||||
// IsShuttingDown tells whether the daemon is shutting down or not
|
||||
|
|
94
daemon/search.go
Normal file
94
daemon/search.go
Normal file
|
@ -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
|
||||
}
|
357
daemon/search_test.go
Normal file
357
daemon/search_test.go
Normal file
|
@ -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
|
||||
// RegistryService is the registry service to use for TLS configuration
|
||||
// and endpoint lookup.
|
||||
RegistryService *registry.Service
|
||||
RegistryService registry.Service
|
||||
// ImageEventLogger notifies events for a given image
|
||||
ImageEventLogger func(id, name, action string)
|
||||
// MetadataStore is the storage backend for distribution-specific
|
||||
|
|
|
@ -31,7 +31,7 @@ type ImagePushConfig struct {
|
|||
ProgressOutput progress.Output
|
||||
// RegistryService is the registry service to use for TLS configuration
|
||||
// and endpoint lookup.
|
||||
RegistryService *registry.Service
|
||||
RegistryService registry.Service
|
||||
// ImageEventLogger notifies events for a given image
|
||||
ImageEventLogger func(id, name, action string)
|
||||
// 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 github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
|
||||
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/imdario/mergo 0.2.1
|
||||
|
||||
|
|
|
@ -661,7 +661,7 @@ func TestMirrorEndpointLookup(t *testing.T) {
|
|||
}
|
||||
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")
|
||||
if err != nil {
|
||||
|
|
|
@ -7,35 +7,50 @@ import (
|
|||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/reference"
|
||||
"github.com/docker/engine-api/types"
|
||||
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.
|
||||
type Service struct {
|
||||
type DefaultService struct {
|
||||
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.
|
||||
func NewService(options ServiceOptions) *Service {
|
||||
return &Service{
|
||||
func NewService(options ServiceOptions) *DefaultService {
|
||||
return &DefaultService{
|
||||
config: newServiceConfig(options),
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceConfig returns the public registry service configuration.
|
||||
func (s *Service) ServiceConfig() *registrytypes.ServiceConfig {
|
||||
func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
||||
return &s.config.ServiceConfig
|
||||
}
|
||||
|
||||
// Auth contacts the public registry with the provided credentials,
|
||||
// and returns OK if authentication was successful.
|
||||
// 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
|
||||
if serverAddress == "" {
|
||||
serverAddress = IndexServer
|
||||
|
@ -93,7 +108,8 @@ func splitReposSearchTerm(reposName string) (string, string) {
|
|||
|
||||
// Search queries the public registry for images matching the specified
|
||||
// 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 {
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -155,25 +171,25 @@ func (e APIEndpoint) ToV1Endpoint(userAgent string, metaHeaders http.Header) (*V
|
|||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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)
|
||||
if err == nil {
|
||||
for _, endpoint := range allEndpoints {
|
||||
|
@ -185,7 +201,7 @@ func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint,
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"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
|
||||
tlsConfig := &cfg
|
||||
if hostname == DefaultNamespace {
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"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
|
||||
tlsConfig := &cfg
|
||||
if hostname == DefaultNamespace || hostname == DefaultV1Registry.Host {
|
||||
|
|
13
vendor/src/github.com/docker/engine-api/client/checkpoint_create.go
vendored
Normal file
13
vendor/src/github.com/docker/engine-api/client/checkpoint_create.go
vendored
Normal file
|
@ -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
|
||||
}
|
12
vendor/src/github.com/docker/engine-api/client/checkpoint_delete.go
vendored
Normal file
12
vendor/src/github.com/docker/engine-api/client/checkpoint_delete.go
vendored
Normal file
|
@ -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
|
||||
}
|
22
vendor/src/github.com/docker/engine-api/client/checkpoint_list.go
vendored
Normal file
22
vendor/src/github.com/docker/engine-api/client/checkpoint_list.go
vendored
Normal file
|
@ -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)
|
||||
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
|
||||
|
||||
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.
|
||||
func (cli *Client) ContainerStart(ctx context.Context, containerID string) error {
|
||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", nil, nil, nil)
|
||||
func (cli *Client) ContainerStart(ctx context.Context, containerID string, checkpointID string) error {
|
||||
query := url.Values{}
|
||||
query.Set("checkpoint", checkpointID)
|
||||
|
||||
resp, err := cli.post(ctx, "/containers/"+containerID+"/start", query, nil, nil)
|
||||
ensureReaderClosed(resp)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ func (cli *Client) Events(ctx context.Context, options types.EventsOptions) (io.
|
|||
query.Set("until", ts)
|
||||
}
|
||||
if options.Filters.Len() > 0 {
|
||||
filterJSON, err := filters.ToParam(options.Filters)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func (cli *Client) ImageList(ctx context.Context, options types.ImageListOptions
|
|||
query := url.Values{}
|
||||
|
||||
if options.Filters.Len() > 0 {
|
||||
filterJSON, err := filters.ToParam(options.Filters)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return images, err
|
||||
}
|
||||
|
|
|
@ -15,6 +15,9 @@ import (
|
|||
// APIClient is an interface that clients that talk with a docker server must implement.
|
||||
type APIClient interface {
|
||||
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)
|
||||
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)
|
||||
|
@ -37,7 +40,7 @@ type APIClient interface {
|
|||
ContainerRestart(ctx context.Context, container string, timeout int) error
|
||||
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, 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
|
||||
ContainerTop(ctx context.Context, container string, arguments []string) (types.ContainerProcessList, 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) {
|
||||
query := url.Values{}
|
||||
if options.Filters.Len() > 0 {
|
||||
filterJSON, err := filters.ToParam(options.Filters)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, options.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -172,6 +172,8 @@ func encodeData(data interface{}) (*bytes.Buffer, error) {
|
|||
|
||||
func ensureReaderClosed(response *serverResponse) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func (cli *Client) VolumeList(ctx context.Context, filter filters.Args) (types.V
|
|||
query := url.Values{}
|
||||
|
||||
if filter.Len() > 0 {
|
||||
filterJSON, err := filters.ToParam(filter)
|
||||
filterJSON, err := filters.ToParamWithVersion(cli.version, filter)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@ import (
|
|||
"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.
|
||||
type ContainerAttachOptions struct {
|
||||
Stream bool
|
||||
|
|
|
@ -257,11 +257,10 @@ type Resources struct {
|
|||
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
||||
|
||||
// Applicable to Windows
|
||||
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||
IOMaximumIOps uint64 // Maximum IOps 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
|
||||
CPUCount int64 `json:"CpuCount"` // CPU count
|
||||
CPUPercent int64 `json:"CpuPercent"` // CPU percent
|
||||
IOMaximumIOps uint64 // Maximum IOps for the container system drive
|
||||
IOMaximumBandwidth uint64 // Maximum IO in bytes per second for the container system drive
|
||||
}
|
||||
|
||||
// UpdateConfig holds the mutable attributes of a Container.
|
||||
|
|
|
@ -215,10 +215,22 @@ func (filters Args) ExactMatch(field, source string) bool {
|
|||
}
|
||||
|
||||
// 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 false
|
||||
if len(filters.fields[field]) != 1 {
|
||||
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,
|
||||
|
|
|
@ -471,3 +471,8 @@ type NetworkDisconnect struct {
|
|||
Container string
|
||||
Force bool
|
||||
}
|
||||
|
||||
// Checkpoint represents the details of a checkpoint
|
||||
type Checkpoint struct {
|
||||
Name string // Name is the name of the checkpoint
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue