mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
client: refactor service api client functions for defined options/result structs
Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
This commit is contained in:
@@ -160,14 +160,14 @@ type PluginAPIClient interface {
|
|||||||
|
|
||||||
// ServiceAPIClient defines API client methods for the services
|
// ServiceAPIClient defines API client methods for the services
|
||||||
type ServiceAPIClient interface {
|
type ServiceAPIClient interface {
|
||||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error)
|
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error)
|
||||||
ServiceInspectWithRaw(ctx context.Context, serviceID string, options ServiceInspectOptions) (swarm.Service, []byte, error)
|
ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error)
|
||||||
ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error)
|
ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, error)
|
||||||
ServiceRemove(ctx context.Context, serviceID string) error
|
ServiceRemove(ctx context.Context, serviceID string, options ServiceRemoveOptions) (ServiceRemoveResult, error)
|
||||||
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error)
|
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (ServiceUpdateResult, error)
|
||||||
ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error)
|
||||||
TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error)
|
||||||
TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error)
|
TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, error)
|
||||||
TaskList(ctx context.Context, options TaskListOptions) (TaskListResult, error)
|
TaskList(ctx context.Context, options TaskListOptions) (TaskListResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,35 +14,59 @@ import (
|
|||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceCreate creates a new service.
|
// ServiceCreateOptions contains the options to use when creating a service.
|
||||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error) {
|
type ServiceCreateOptions struct {
|
||||||
var response swarm.ServiceCreateResponse
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateResult represents the result of creating a service.
|
||||||
|
type ServiceCreateResult struct {
|
||||||
|
// ID is the ID of the created service.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Warnings is a list of warnings that occurred during service creation.
|
||||||
|
Warnings []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreate creates a new service.
|
||||||
|
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error) {
|
||||||
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
||||||
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
||||||
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateServiceSpec(service); err != nil {
|
if err := validateServiceSpec(service); err != nil {
|
||||||
return response, err
|
return ServiceCreateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that the image is tagged
|
// ensure that the image is tagged
|
||||||
var resolveWarning string
|
var warnings []string
|
||||||
switch {
|
switch {
|
||||||
case service.TaskTemplate.ContainerSpec != nil:
|
case service.TaskTemplate.ContainerSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
case service.TaskTemplate.PluginSpec != nil:
|
case service.TaskTemplate.PluginSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,15 +77,17 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
|||||||
resp, err := cli.post(ctx, "/services/create", nil, service, headers)
|
resp, err := cli.post(ctx, "/services/create", nil, service, headers)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return ServiceCreateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response swarm.ServiceCreateResponse
|
||||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
if resolveWarning != "" {
|
warnings = append(warnings, response.Warnings...)
|
||||||
response.Warnings = append(response.Warnings, resolveWarning)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, err
|
return ServiceCreateResult{
|
||||||
|
ID: response.ID,
|
||||||
|
Warnings: warnings,
|
||||||
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
||||||
|
|||||||
@@ -1,38 +1,40 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceInspectWithRaw returns the service information and the raw data.
|
// ServiceInspectOptions holds parameters related to the service inspect operation.
|
||||||
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts ServiceInspectOptions) (swarm.Service, []byte, error) {
|
type ServiceInspectOptions struct {
|
||||||
|
InsertDefaults bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspectResult represents the result of a service inspect operation.
|
||||||
|
type ServiceInspectResult struct {
|
||||||
|
Service swarm.Service
|
||||||
|
Raw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspect retrieves detailed information about a specific service by its ID.
|
||||||
|
func (cli *Client) ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.Service{}, nil, err
|
return ServiceInspectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
query.Set("insertDefaults", fmt.Sprintf("%v", options.InsertDefaults))
|
||||||
resp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
resp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.Service{}, nil, err
|
return ServiceInspectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
var out ServiceInspectResult
|
||||||
if err != nil {
|
out.Raw, err = decodeWithRaw(resp, &out.Service)
|
||||||
return swarm.Service{}, nil, err
|
return out, err
|
||||||
}
|
|
||||||
|
|
||||||
var response swarm.Service
|
|
||||||
rdr := bytes.NewReader(body)
|
|
||||||
err = json.NewDecoder(rdr).Decode(&response)
|
|
||||||
return response, body, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestServiceInspectError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, _, err = client.ServiceInspectWithRaw(context.Background(), "nothing", ServiceInspectOptions{})
|
_, err = client.ServiceInspect(context.Background(), "nothing", ServiceInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ func TestServiceInspectServiceNotFound(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, _, err = client.ServiceInspectWithRaw(context.Background(), "unknown", ServiceInspectOptions{})
|
_, err = client.ServiceInspect(context.Background(), "unknown", ServiceInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,11 +36,11 @@ func TestServiceInspectWithEmptyID(t *testing.T) {
|
|||||||
return nil, errors.New("should not make request")
|
return nil, errors.New("should not make request")
|
||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, _, err = client.ServiceInspectWithRaw(context.Background(), "", ServiceInspectOptions{})
|
_, err = client.ServiceInspect(context.Background(), "", ServiceInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
_, _, err = client.ServiceInspectWithRaw(context.Background(), " ", ServiceInspectOptions{})
|
_, err = client.ServiceInspect(context.Background(), " ", ServiceInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ func TestServiceInspect(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
serviceInspect, _, err := client.ServiceInspectWithRaw(context.Background(), "service_id", ServiceInspectOptions{})
|
inspect, err := client.ServiceInspect(context.Background(), "service_id", ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(serviceInspect.ID, "service_id"))
|
assert.Check(t, is.Equal(inspect.Service.ID, "service_id"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,22 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceListOptions holds parameters to list services with.
|
||||||
|
type ServiceListOptions struct {
|
||||||
|
Filters Filters
|
||||||
|
|
||||||
|
// Status indicates whether the server should include the service task
|
||||||
|
// count of running and desired tasks.
|
||||||
|
Status bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceListResult represents the result of a service list operation.
|
||||||
|
type ServiceListResult struct {
|
||||||
|
Services []swarm.Service
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceList returns the list of services.
|
// ServiceList returns the list of services.
|
||||||
func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error) {
|
func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
options.Filters.updateURLValues(query)
|
options.Filters.updateURLValues(query)
|
||||||
@@ -21,10 +35,10 @@ func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions)
|
|||||||
resp, err := cli.get(ctx, "/services", query, nil)
|
resp, err := cli.get(ctx, "/services", query, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceListResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var services []swarm.Service
|
var services []swarm.Service
|
||||||
err = json.NewDecoder(resp.Body).Decode(&services)
|
err = json.NewDecoder(resp.Body).Decode(&services)
|
||||||
return services, err
|
return ServiceListResult{Services: services}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ func TestServiceList(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
services, err := client.ServiceList(context.Background(), listCase.options)
|
list, err := client.ServiceList(context.Background(), listCase.options)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Len(services, 2))
|
assert.Check(t, is.Len(list.Services, 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,37 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/moby/moby/client/internal/timestamp"
|
"github.com/moby/moby/client/internal/timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceLogs returns the logs generated by a service in an [io.ReadCloser].
|
// ServiceLogsOptions holds parameters to filter logs with.
|
||||||
|
type ServiceLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceLogsResult holds the result of a service logs operation.
|
||||||
|
// It implements [io.ReadCloser].
|
||||||
// It's up to the caller to close the stream.
|
// It's up to the caller to close the stream.
|
||||||
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error) {
|
type ServiceLogsResult struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
close func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceLogs returns the logs generated by a service in an [ServiceLogsResult].
|
||||||
|
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceLogsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
@@ -30,7 +50,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
|||||||
if options.Since != "" {
|
if options.Since != "" {
|
||||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`invalid value for "since": %w`, err)
|
return ServiceLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err)
|
||||||
}
|
}
|
||||||
query.Set("since", ts)
|
query.Set("since", ts)
|
||||||
}
|
}
|
||||||
@@ -50,7 +70,33 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
|||||||
|
|
||||||
resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil)
|
resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceLogsResult{}, err
|
||||||
}
|
}
|
||||||
return resp.Body, nil
|
return newServiceLogsResult(resp.Body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServiceLogsResult(rc io.ReadCloser) ServiceLogsResult {
|
||||||
|
if rc == nil {
|
||||||
|
panic("nil io.ReadCloser")
|
||||||
|
}
|
||||||
|
return ServiceLogsResult{
|
||||||
|
rc: rc,
|
||||||
|
close: sync.OnceValue(rc.Close),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r ServiceLogsResult) Read(p []byte) (n int, err error) {
|
||||||
|
if r.rc == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.rc.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r ServiceLogsResult) Close() error {
|
||||||
|
if r.close == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,19 +20,19 @@ import (
|
|||||||
func TestServiceLogsError(t *testing.T) {
|
func TestServiceLogsError(t *testing.T) {
|
||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, err = client.ServiceLogs(context.Background(), "service_id", ContainerLogsOptions{})
|
_, err = client.ServiceLogs(context.Background(), "service_id", ServiceLogsOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||||
|
|
||||||
_, err = client.ServiceLogs(context.Background(), "service_id", ContainerLogsOptions{
|
_, err = client.ServiceLogs(context.Background(), "service_id", ServiceLogsOptions{
|
||||||
Since: "2006-01-02TZ",
|
Since: "2006-01-02TZ",
|
||||||
})
|
})
|
||||||
assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`))
|
assert.Check(t, is.ErrorContains(err, `parsing time "2006-01-02TZ"`))
|
||||||
|
|
||||||
_, err = client.ServiceLogs(context.Background(), "", ContainerLogsOptions{})
|
_, err = client.ServiceLogs(context.Background(), "", ServiceLogsOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
_, err = client.ServiceLogs(context.Background(), " ", ContainerLogsOptions{})
|
_, err = client.ServiceLogs(context.Background(), " ", ServiceLogsOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ func TestServiceLogsError(t *testing.T) {
|
|||||||
func TestServiceLogs(t *testing.T) {
|
func TestServiceLogs(t *testing.T) {
|
||||||
const expectedURL = "/services/service_id/logs"
|
const expectedURL = "/services/service_id/logs"
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
options ContainerLogsOptions
|
options ServiceLogsOptions
|
||||||
expectedQueryParams map[string]string
|
expectedQueryParams map[string]string
|
||||||
expectedError string
|
expectedError string
|
||||||
}{
|
}{
|
||||||
@@ -50,7 +50,7 @@ func TestServiceLogs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: ContainerLogsOptions{
|
options: ServiceLogsOptions{
|
||||||
Tail: "any",
|
Tail: "any",
|
||||||
},
|
},
|
||||||
expectedQueryParams: map[string]string{
|
expectedQueryParams: map[string]string{
|
||||||
@@ -58,7 +58,7 @@ func TestServiceLogs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: ContainerLogsOptions{
|
options: ServiceLogsOptions{
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
ShowStderr: true,
|
ShowStderr: true,
|
||||||
Timestamps: true,
|
Timestamps: true,
|
||||||
@@ -75,7 +75,7 @@ func TestServiceLogs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: ContainerLogsOptions{
|
options: ServiceLogsOptions{
|
||||||
// timestamp is passed as-is
|
// timestamp is passed as-is
|
||||||
Since: "1136073600.000000001",
|
Since: "1136073600.000000001",
|
||||||
},
|
},
|
||||||
@@ -85,7 +85,7 @@ func TestServiceLogs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
options: ContainerLogsOptions{
|
options: ServiceLogsOptions{
|
||||||
// invalid dates are not passed.
|
// invalid dates are not passed.
|
||||||
Since: "invalid value",
|
Since: "invalid value",
|
||||||
},
|
},
|
||||||
@@ -129,7 +129,7 @@ func ExampleClient_ServiceLogs_withTimeout() {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
client, _ := NewClientWithOpts(FromEnv)
|
client, _ := NewClientWithOpts(FromEnv)
|
||||||
reader, err := client.ServiceLogs(ctx, "service_id", ContainerLogsOptions{})
|
reader, err := client.ServiceLogs(ctx, "service_id", ServiceLogsOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,24 @@ package client
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
|
// ServiceRemoveOptions contains options for removing a service.
|
||||||
|
type ServiceRemoveOptions struct {
|
||||||
|
// No options currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceRemoveResult contains the result of removing a service.
|
||||||
|
type ServiceRemoveResult struct {
|
||||||
|
// No fields currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceRemove kills and removes a service.
|
// ServiceRemove kills and removes a service.
|
||||||
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string, options ServiceRemoveOptions) (ServiceRemoveResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ServiceRemoveResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
return err
|
return ServiceRemoveResult{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ func TestServiceRemoveError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = client.ServiceRemove(context.Background(), "service_id")
|
_, err = client.ServiceRemove(context.Background(), "service_id", ServiceRemoveOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||||
|
|
||||||
err = client.ServiceRemove(context.Background(), "")
|
_, err = client.ServiceRemove(context.Background(), "", ServiceRemoveOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
err = client.ServiceRemove(context.Background(), " ")
|
_, err = client.ServiceRemove(context.Background(), " ", ServiceRemoveOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ func TestServiceRemoveNotFoundError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "no such service: service_id")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "no such service: service_id")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = client.ServiceRemove(context.Background(), "service_id")
|
_, err = client.ServiceRemove(context.Background(), "service_id", ServiceRemoveOptions{})
|
||||||
assert.Check(t, is.ErrorContains(err, "no such service: service_id"))
|
assert.Check(t, is.ErrorContains(err, "no such service: service_id"))
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsNotFound))
|
||||||
}
|
}
|
||||||
@@ -51,6 +51,6 @@ func TestServiceRemove(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = client.ServiceRemove(context.Background(), "service_id")
|
_, err = client.ServiceRemove(context.Background(), "service_id", ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,18 +10,54 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceUpdateOptions contains the options to be used for updating services.
|
||||||
|
type ServiceUpdateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
||||||
|
// into this field. While it does open API users up to racy writes, most
|
||||||
|
// users may not need that level of consistency in practice.
|
||||||
|
|
||||||
|
// RegistryAuthFrom specifies where to find the registry authorization
|
||||||
|
// credentials if they are not given in EncodedRegistryAuth. Valid
|
||||||
|
// values are "spec" and "previous-spec".
|
||||||
|
RegistryAuthFrom string
|
||||||
|
|
||||||
|
// Rollback indicates whether a server-side rollback should be
|
||||||
|
// performed. When this is set, the provided spec will be ignored.
|
||||||
|
// The valid values are "previous" and "none". An empty value is the
|
||||||
|
// same as "none".
|
||||||
|
Rollback string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceUpdateResult represents the result of a service update.
|
||||||
|
type ServiceUpdateResult struct {
|
||||||
|
// Warnings contains any warnings that occurred during the update.
|
||||||
|
Warnings []string
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceUpdate updates a Service. The version number is required to avoid
|
// ServiceUpdate updates a Service. The version number is required to avoid
|
||||||
// conflicting writes. It must be the value as set *before* the update.
|
// conflicting writes. It must be the value as set *before* the update.
|
||||||
// You can find this value in the [swarm.Service.Meta] field, which can
|
// You can find this value in the [swarm.Service.Meta] field, which can
|
||||||
// be found using [Client.ServiceInspectWithRaw].
|
// be found using [Client.ServiceInspectWithRaw].
|
||||||
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) {
|
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (ServiceUpdateResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateServiceSpec(service); err != nil {
|
if err := validateServiceSpec(service); err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
@@ -36,21 +72,23 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
query.Set("version", version.String())
|
query.Set("version", version.String())
|
||||||
|
|
||||||
// ensure that the image is tagged
|
// ensure that the image is tagged
|
||||||
var resolveWarning string
|
var warnings []string
|
||||||
switch {
|
switch {
|
||||||
case service.TaskTemplate.ContainerSpec != nil:
|
case service.TaskTemplate.ContainerSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
case service.TaskTemplate.PluginSpec != nil:
|
case service.TaskTemplate.PluginSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +99,11 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var response swarm.ServiceUpdateResponse
|
var response swarm.ServiceUpdateResponse
|
||||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
if resolveWarning != "" {
|
warnings = append(warnings, response.Warnings...)
|
||||||
response.Warnings = append(response.Warnings, resolveWarning)
|
return ServiceUpdateResult{Warnings: warnings}, err
|
||||||
}
|
|
||||||
|
|
||||||
return response, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ type SwarmJoinOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwarmJoinResult contains the result of joining a swarm.
|
// SwarmJoinResult contains the result of joining a swarm.
|
||||||
type SwarmJoinResult struct{}
|
type SwarmJoinResult struct {
|
||||||
|
// No fields currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
// SwarmJoin joins the swarm.
|
// SwarmJoin joins the swarm.
|
||||||
func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) {
|
func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceCreateOptions contains the options to use when creating a service.
|
|
||||||
type ServiceCreateOptions struct {
|
|
||||||
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
|
||||||
// use when updating the service.
|
|
||||||
//
|
|
||||||
// This field follows the format of the X-Registry-Auth header.
|
|
||||||
EncodedRegistryAuth string
|
|
||||||
|
|
||||||
// QueryRegistry indicates whether the service update requires
|
|
||||||
// contacting a registry. A registry may be contacted to retrieve
|
|
||||||
// the image digest and manifest, which in turn can be used to update
|
|
||||||
// platform or other information about the service.
|
|
||||||
QueryRegistry bool
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceInspectOptions holds parameters related to the "service inspect"
|
|
||||||
// operation.
|
|
||||||
type ServiceInspectOptions struct {
|
|
||||||
InsertDefaults bool
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceListOptions holds parameters to list services with.
|
|
||||||
type ServiceListOptions struct {
|
|
||||||
Filters Filters
|
|
||||||
|
|
||||||
// Status indicates whether the server should include the service task
|
|
||||||
// count of running and desired tasks.
|
|
||||||
Status bool
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceUpdateOptions contains the options to be used for updating services.
|
|
||||||
type ServiceUpdateOptions struct {
|
|
||||||
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
|
||||||
// use when updating the service.
|
|
||||||
//
|
|
||||||
// This field follows the format of the X-Registry-Auth header.
|
|
||||||
EncodedRegistryAuth string
|
|
||||||
|
|
||||||
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
|
||||||
// into this field. While it does open API users up to racy writes, most
|
|
||||||
// users may not need that level of consistency in practice.
|
|
||||||
|
|
||||||
// RegistryAuthFrom specifies where to find the registry authorization
|
|
||||||
// credentials if they are not given in EncodedRegistryAuth. Valid
|
|
||||||
// values are "spec" and "previous-spec".
|
|
||||||
RegistryAuthFrom string
|
|
||||||
|
|
||||||
// Rollback indicates whether a server-side rollback should be
|
|
||||||
// performed. When this is set, the provided spec will be ignored.
|
|
||||||
// The valid values are "previous" and "none". An empty value is the
|
|
||||||
// same as "none".
|
|
||||||
Rollback string
|
|
||||||
|
|
||||||
// QueryRegistry indicates whether the service update requires
|
|
||||||
// contacting a registry. A registry may be contacted to retrieve
|
|
||||||
// the image digest and manifest, which in turn can be used to update
|
|
||||||
// platform or other information about the service.
|
|
||||||
QueryRegistry bool
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,11 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TaskInspectOptions contains options for inspecting a task.
|
||||||
|
type TaskInspectOptions struct {
|
||||||
|
// Currently no options are defined.
|
||||||
|
}
|
||||||
|
|
||||||
// TaskInspectResult contains the result of a task inspection.
|
// TaskInspectResult contains the result of a task inspection.
|
||||||
type TaskInspectResult struct {
|
type TaskInspectResult struct {
|
||||||
Task swarm.Task
|
Task swarm.Task
|
||||||
@@ -13,7 +18,7 @@ type TaskInspectResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TaskInspect returns the task information and its raw representation.
|
// TaskInspect returns the task information and its raw representation.
|
||||||
func (cli *Client) TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error) {
|
func (cli *Client) TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, error) {
|
||||||
taskID, err := trimID("task", taskID)
|
taskID, err := trimID("task", taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TaskInspectResult{}, err
|
return TaskInspectResult{}, err
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func TestTaskInspectError(t *testing.T) {
|
|||||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
_, err = client.TaskInspect(context.Background(), "nothing")
|
_, err = client.TaskInspect(context.Background(), "nothing", TaskInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,11 +28,11 @@ func TestTaskInspectWithEmptyID(t *testing.T) {
|
|||||||
return nil, errors.New("should not make request")
|
return nil, errors.New("should not make request")
|
||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
_, err = client.TaskInspect(context.Background(), "")
|
_, err = client.TaskInspect(context.Background(), "", TaskInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
|
|
||||||
_, err = client.TaskInspect(context.Background(), " ")
|
_, err = client.TaskInspect(context.Background(), " ", TaskInspectOptions{})
|
||||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||||
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
assert.Check(t, is.ErrorContains(err, "value is empty"))
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ func TestTaskInspect(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
result, err := client.TaskInspect(context.Background(), "task_id")
|
result, err := client.TaskInspect(context.Background(), "task_id", TaskInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(result.Task.ID, "task_id"))
|
assert.Check(t, is.Equal(result.Task.ID, "task_id"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type TaskListOptions struct {
|
|||||||
|
|
||||||
// TaskListResult contains the result of a task list operation.
|
// TaskListResult contains the result of a task list operation.
|
||||||
type TaskListResult struct {
|
type TaskListResult struct {
|
||||||
Tasks []swarm.Task
|
Items []swarm.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskList returns the list of tasks.
|
// TaskList returns the list of tasks.
|
||||||
@@ -32,5 +32,5 @@ func (cli *Client) TaskList(ctx context.Context, options TaskListOptions) (TaskL
|
|||||||
|
|
||||||
var tasks []swarm.Task
|
var tasks []swarm.Task
|
||||||
err = json.NewDecoder(resp.Body).Decode(&tasks)
|
err = json.NewDecoder(resp.Body).Decode(&tasks)
|
||||||
return TaskListResult{Tasks: tasks}, err
|
return TaskListResult{Items: tasks}, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,6 @@ func TestTaskList(t *testing.T) {
|
|||||||
|
|
||||||
result, err := client.TaskList(context.Background(), listCase.options)
|
result, err := client.TaskList(context.Background(), listCase.options)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Len(result.Tasks, 2))
|
assert.Check(t, is.Len(result.Items, 2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,34 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/moby/moby/client/internal/timestamp"
|
"github.com/moby/moby/client/internal/timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskLogs returns the logs generated by a task in an [io.ReadCloser].
|
// TaskLogsOptions holds parameters to filter logs with.
|
||||||
|
type TaskLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskLogsResult holds the result of a task logs operation.
|
||||||
|
// It implements [io.ReadCloser].
|
||||||
|
type TaskLogsResult struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
close func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskLogs returns the logs generated by a task.
|
||||||
// It's up to the caller to close the stream.
|
// It's up to the caller to close the stream.
|
||||||
func (cli *Client) TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error) {
|
func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.ShowStdout {
|
if options.ShowStdout {
|
||||||
query.Set("stdout", "1")
|
query.Set("stdout", "1")
|
||||||
@@ -24,7 +44,7 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
|||||||
if options.Since != "" {
|
if options.Since != "" {
|
||||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return TaskLogsResult{}, err
|
||||||
}
|
}
|
||||||
query.Set("since", ts)
|
query.Set("since", ts)
|
||||||
}
|
}
|
||||||
@@ -44,7 +64,33 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
|||||||
|
|
||||||
resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil)
|
resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return TaskLogsResult{}, err
|
||||||
}
|
}
|
||||||
return resp.Body, nil
|
return newTaskLogsResult(resp.Body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTaskLogsResult(rc io.ReadCloser) TaskLogsResult {
|
||||||
|
if rc == nil {
|
||||||
|
panic("nil io.ReadCloser")
|
||||||
|
}
|
||||||
|
return TaskLogsResult{
|
||||||
|
rc: rc,
|
||||||
|
close: sync.OnceValue(rc.Close),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r TaskLogsResult) Read(p []byte) (n int, err error) {
|
||||||
|
if r.rc == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.rc.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r TaskLogsResult) Close() error {
|
||||||
|
if r.close == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,13 +103,13 @@ func (d *Daemon) CheckRunningTaskNetworks(ctx context.Context) func(t *testing.T
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
taskResult, err := cli.TaskList(ctx, client.TaskListOptions{
|
taskList, err := cli.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("desired-state", "running"),
|
Filters: make(client.Filters).Add("desired-state", "running"),
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
result := make(map[string]int)
|
result := make(map[string]int)
|
||||||
for _, task := range taskResult.Tasks {
|
for _, task := range taskList.Items {
|
||||||
for _, network := range task.Spec.Networks {
|
for _, network := range task.Spec.Networks {
|
||||||
result[network.Target]++
|
result[network.Target]++
|
||||||
}
|
}
|
||||||
@@ -124,13 +124,13 @@ func (d *Daemon) CheckRunningTaskImages(ctx context.Context) func(t *testing.T)
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
taskResult, err := cli.TaskList(ctx, client.TaskListOptions{
|
taskList, err := cli.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("desired-state", "running"),
|
Filters: make(client.Filters).Add("desired-state", "running"),
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
result := make(map[string]int)
|
result := make(map[string]int)
|
||||||
for _, task := range taskResult.Tasks {
|
for _, task := range taskList.Items {
|
||||||
if task.Status.State == swarm.TaskStateRunning && task.Spec.ContainerSpec != nil {
|
if task.Status.State == swarm.TaskStateRunning && task.Spec.ContainerSpec != nil {
|
||||||
result[task.Spec.ContainerSpec.Image]++
|
result[task.Spec.ContainerSpec.Image]++
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,14 +78,14 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *testing.T) {
|
|||||||
options := client.ServiceInspectOptions{InsertDefaults: true}
|
options := client.ServiceInspectOptions{InsertDefaults: true}
|
||||||
|
|
||||||
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
||||||
resp, _, err := apiClient.ServiceInspectWithRaw(ctx, id, options)
|
res, err := apiClient.ServiceInspect(ctx, id, options)
|
||||||
out := fmt.Sprintf("%+v", resp)
|
out := fmt.Sprintf("%+v", res.Service)
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
assert.Assert(c, is.Contains(out, "UpdateConfig"))
|
assert.Assert(c, is.Contains(out, "UpdateConfig"))
|
||||||
|
|
||||||
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
||||||
resp, _, err = apiClient.ServiceInspectWithRaw(ctx, "top", options)
|
res, err = apiClient.ServiceInspect(ctx, "top", options)
|
||||||
out = fmt.Sprintf("%+v", resp)
|
out = fmt.Sprintf("%+v", res.Service)
|
||||||
assert.NilError(c, err)
|
assert.NilError(c, err)
|
||||||
assert.Assert(c, is.Contains(out, "UpdateConfig"))
|
assert.Assert(c, is.Contains(out, "UpdateConfig"))
|
||||||
|
|
||||||
|
|||||||
@@ -204,14 +204,14 @@ func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
|
|||||||
func GetRunningTasks(ctx context.Context, t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
|
func GetRunningTasks(ctx context.Context, t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
result, err := c.TaskList(ctx, client.TaskListOptions{
|
taskList, err := c.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).
|
Filters: make(client.Filters).
|
||||||
Add("service", serviceID).
|
Add("service", serviceID).
|
||||||
Add("desired-state", "running"),
|
Add("desired-state", "running"),
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return result.Tasks
|
return taskList.Items
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecTask runs the passed in exec config on the given task
|
// ExecTask runs the passed in exec config on the given task
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ import (
|
|||||||
// NoTasksForService verifies that there are no more tasks for the given service
|
// NoTasksForService verifies that there are no more tasks for the given service
|
||||||
func NoTasksForService(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
func NoTasksForService(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", serviceID),
|
Filters: make(client.Filters).Add("service", serviceID),
|
||||||
})
|
})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if len(result.Tasks) == 0 {
|
if len(taskList.Items) == 0 {
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
}
|
}
|
||||||
if len(result.Tasks) > 0 {
|
if len(taskList.Items) > 0 {
|
||||||
return poll.Continue("task count for service %s at %d waiting for 0", serviceID, len(result.Tasks))
|
return poll.Continue("task count for service %s at %d waiting for 0", serviceID, len(taskList.Items))
|
||||||
}
|
}
|
||||||
return poll.Continue("waiting for tasks for service %s to be deleted", serviceID)
|
return poll.Continue("waiting for tasks for service %s to be deleted", serviceID)
|
||||||
}
|
}
|
||||||
@@ -32,14 +32,14 @@ func NoTasksForService(ctx context.Context, apiClient client.ServiceAPIClient, s
|
|||||||
// NoTasks verifies that all tasks are gone
|
// NoTasks verifies that all tasks are gone
|
||||||
func NoTasks(ctx context.Context, apiClient client.ServiceAPIClient) func(log poll.LogT) poll.Result {
|
func NoTasks(ctx context.Context, apiClient client.ServiceAPIClient) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{})
|
taskResult, err := apiClient.TaskList(ctx, client.TaskListOptions{})
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return poll.Error(err)
|
return poll.Error(err)
|
||||||
case len(result.Tasks) == 0:
|
case len(taskResult.Items) == 0:
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
default:
|
default:
|
||||||
return poll.Continue("waiting for all tasks to be removed: task count at %d", len(result.Tasks))
|
return poll.Continue("waiting for all tasks to be removed: task count at %d", len(taskResult.Items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,12 +47,12 @@ func NoTasks(ctx context.Context, apiClient client.ServiceAPIClient) func(log po
|
|||||||
// RunningTasksCount verifies there are `instances` tasks running for `serviceID`
|
// RunningTasksCount verifies there are `instances` tasks running for `serviceID`
|
||||||
func RunningTasksCount(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
|
func RunningTasksCount(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", serviceID),
|
Filters: make(client.Filters).Add("service", serviceID),
|
||||||
})
|
})
|
||||||
var running int
|
var running int
|
||||||
var taskError string
|
var taskError string
|
||||||
for _, task := range result.Tasks {
|
for _, task := range taskList.Items {
|
||||||
switch task.Status.State {
|
switch task.Status.State {
|
||||||
case swarmtypes.TaskStateRunning:
|
case swarmtypes.TaskStateRunning:
|
||||||
running++
|
running++
|
||||||
@@ -76,7 +76,7 @@ func RunningTasksCount(ctx context.Context, apiClient client.ServiceAPIClient, s
|
|||||||
case running == int(instances):
|
case running == int(instances):
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
default:
|
default:
|
||||||
return poll.Continue("running task count at %d waiting for %d (total tasks: %d)", running, instances, len(result.Tasks))
|
return poll.Continue("running task count at %d waiting for %d (total tasks: %d)", running, instances, len(taskList.Items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ func JobComplete(ctx context.Context, apiClient client.ServiceAPIClient, service
|
|||||||
previousResult := ""
|
previousResult := ""
|
||||||
|
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: filter,
|
Filters: filter,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -110,7 +110,7 @@ func JobComplete(ctx context.Context, apiClient client.ServiceAPIClient, service
|
|||||||
var runningSlot []int
|
var runningSlot []int
|
||||||
var runningID []string
|
var runningID []string
|
||||||
|
|
||||||
for _, task := range result.Tasks {
|
for _, task := range taskList.Items {
|
||||||
// make sure the task has the same job iteration
|
// make sure the task has the same job iteration
|
||||||
if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index {
|
if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ func TestInspectNetwork(t *testing.T) {
|
|||||||
swarm.ServiceWithNetwork(networkName),
|
swarm.ServiceWithNetwork(networkName),
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
assert.NilError(t, c1.ServiceRemove(ctx, serviceID))
|
_, err := c1.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
|
assert.NilError(t, err)
|
||||||
poll.WaitOn(t, swarm.NoTasksForService(ctx, c1, serviceID), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasksForService(ctx, c1, serviceID), swarm.ServicePoll)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func TestHostPortMappings(t *testing.T) {
|
|||||||
{Protocol: networktypes.TCP, TargetPort: 80, PublishedPort: 80, PublishMode: swarmtypes.PortConfigPublishModeHost},
|
{Protocol: networktypes.TCP, TargetPort: 80, PublishedPort: 80, PublishMode: swarmtypes.PortConfigPublishModeHost},
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
defer apiClient.ServiceRemove(ctx, svcID)
|
defer apiClient.ServiceRemove(ctx, svcID, client.ServiceRemoveOptions{})
|
||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, svcID, 1), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, svcID, 1), swarm.ServicePoll)
|
||||||
|
|
||||||
|
|||||||
@@ -245,10 +245,10 @@ func TestServiceWithPredefinedNetwork(t *testing.T) {
|
|||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
_, _, err := c.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
_, err := c.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = c.ServiceRemove(ctx, serviceID)
|
_, err = c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,10 +286,10 @@ func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
|
|||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, c, serviceID, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
_, _, err := c.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
_, err := c.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = c.ServiceRemove(ctx, serviceID)
|
_, err = c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
|
poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
|
||||||
@@ -333,14 +333,14 @@ func swarmIngressReady(ctx context.Context, apiClient client.NetworkAPIClient) f
|
|||||||
|
|
||||||
func noServices(ctx context.Context, apiClient client.ServiceAPIClient) func(log poll.LogT) poll.Result {
|
func noServices(ctx context.Context, apiClient client.ServiceAPIClient) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
services, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
result, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return poll.Error(err)
|
return poll.Error(err)
|
||||||
case len(services) == 0:
|
case len(result.Services) == 0:
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
default:
|
default:
|
||||||
return poll.Continue("waiting for all services to be removed: service count at %d", len(services))
|
return poll.Continue("waiting for all services to be removed: service count at %d", len(result.Services))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,7 +369,7 @@ func TestServiceWithDataPathPortInit(t *testing.T) {
|
|||||||
|
|
||||||
info := d.Info(t)
|
info := d.Info(t)
|
||||||
assert.Equal(t, info.Swarm.Cluster.DataPathPort, datapathPort)
|
assert.Equal(t, info.Swarm.Cluster.DataPathPort, datapathPort)
|
||||||
err := c.ServiceRemove(ctx, serviceID)
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
|
poll.WaitOn(t, noServices(ctx, c), swarm.ServicePoll)
|
||||||
poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasks(ctx, c), swarm.ServicePoll)
|
||||||
@@ -402,7 +402,7 @@ func TestServiceWithDataPathPortInit(t *testing.T) {
|
|||||||
info = d.Info(t)
|
info = d.Info(t)
|
||||||
var defaultDataPathPort uint32 = 4789
|
var defaultDataPathPort uint32 = 4789
|
||||||
assert.Equal(t, info.Swarm.Cluster.DataPathPort, defaultDataPathPort)
|
assert.Equal(t, info.Swarm.Cluster.DataPathPort, defaultDataPathPort)
|
||||||
err = nc.ServiceRemove(ctx, serviceID)
|
_, err = nc.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
poll.WaitOn(t, noServices(ctx, nc), swarm.ServicePoll)
|
poll.WaitOn(t, noServices(ctx, nc), swarm.ServicePoll)
|
||||||
poll.WaitOn(t, swarm.NoTasks(ctx, nc), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasks(ctx, nc), swarm.ServicePoll)
|
||||||
@@ -440,7 +440,7 @@ func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
|
|||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, cli, serviceID, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
_, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
_, err := cli.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
res, err := cli.NetworkInspect(ctx, overlayID, client.NetworkInspectOptions{Verbose: true})
|
res, err := cli.NetworkInspect(ctx, overlayID, client.NetworkInspectOptions{Verbose: true})
|
||||||
@@ -459,7 +459,7 @@ func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
|
|||||||
assert.Assert(t, len(res.Network.IPAM.Config) > 0)
|
assert.Assert(t, len(res.Network.IPAM.Config) > 0)
|
||||||
assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.0.0/24"))
|
assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.0.0/24"))
|
||||||
|
|
||||||
err = cli.ServiceRemove(ctx, serviceID)
|
_, err = cli.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
|
poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
|
||||||
poll.WaitOn(t, swarm.NoTasks(ctx, cli), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasks(ctx, cli), swarm.ServicePoll)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|||||||
@@ -100,10 +100,10 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
|
|||||||
serviceID := swarm.CreateService(ctx, t, d, serviceSpec...)
|
serviceID := swarm.CreateService(ctx, t, d, serviceSpec...)
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
_, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
_, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID), swarm.ServicePoll)
|
||||||
@@ -111,7 +111,7 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
|
|||||||
serviceID2 := swarm.CreateService(ctx, t, d, serviceSpec...)
|
serviceID2 := swarm.CreateService(ctx, t, d, serviceSpec...)
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID2, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID2, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID2)
|
_, err = apiClient.ServiceRemove(ctx, serviceID2, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
// we can't just wait on no tasks for the service, counter-intuitively.
|
// we can't just wait on no tasks for the service, counter-intuitively.
|
||||||
@@ -185,7 +185,7 @@ func TestCreateServiceMaxReplicas(t *testing.T) {
|
|||||||
serviceID := swarm.CreateService(ctx, t, d, serviceSpec...)
|
serviceID := swarm.CreateService(ctx, t, d, serviceSpec...)
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, maxReplicas), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, maxReplicas), swarm.ServicePoll)
|
||||||
|
|
||||||
_, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
_, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +227,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
|
|||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances), swarm.ServicePoll)
|
||||||
|
|
||||||
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ContainerLogsOptions{
|
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ServiceLogsOptions{
|
||||||
Tail: "1",
|
Tail: "1",
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
})
|
})
|
||||||
@@ -238,7 +238,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
|
|||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID), swarm.ServicePoll)
|
||||||
|
|
||||||
@@ -286,7 +286,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
|
|||||||
|
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances))
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, serviceID, instances))
|
||||||
|
|
||||||
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ContainerLogsOptions{
|
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ServiceLogsOptions{
|
||||||
Tail: "1",
|
Tail: "1",
|
||||||
ShowStdout: true,
|
ShowStdout: true,
|
||||||
})
|
})
|
||||||
@@ -297,7 +297,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
|
|||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID))
|
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID))
|
||||||
|
|
||||||
@@ -368,25 +368,25 @@ func TestCreateServiceSysctls(t *testing.T) {
|
|||||||
// more complex)
|
// more complex)
|
||||||
|
|
||||||
// get all tasks of the service, so we can get the container
|
// get all tasks of the service, so we can get the container
|
||||||
taskResult, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", serviceID),
|
Filters: make(client.Filters).Add("service", serviceID),
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(len(taskResult.Tasks), 1))
|
assert.Check(t, is.Equal(len(taskList.Items), 1))
|
||||||
|
|
||||||
// verify that the container has the sysctl option set
|
// verify that the container has the sysctl option set
|
||||||
ctnr, err := apiClient.ContainerInspect(ctx, taskResult.Tasks[0].Status.ContainerStatus.ContainerID)
|
ctnr, err := apiClient.ContainerInspect(ctx, taskList.Items[0].Status.ContainerStatus.ContainerID)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, ctnr.HostConfig.Sysctls, expectedSysctls)
|
assert.DeepEqual(t, ctnr.HostConfig.Sysctls, expectedSysctls)
|
||||||
|
|
||||||
// verify that the task has the sysctl option set in the task object
|
// verify that the task has the sysctl option set in the task object
|
||||||
assert.DeepEqual(t, taskResult.Tasks[0].Spec.ContainerSpec.Sysctls, expectedSysctls)
|
assert.DeepEqual(t, taskList.Items[0].Spec.ContainerSpec.Sysctls, expectedSysctls)
|
||||||
|
|
||||||
// verify that the service also has the sysctl set in the spec.
|
// verify that the service also has the sysctl set in the spec.
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t,
|
assert.DeepEqual(t,
|
||||||
service.Spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls,
|
result.Service.Spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,25 +438,25 @@ func TestCreateServiceCapabilities(t *testing.T) {
|
|||||||
// level has been tested elsewhere.
|
// level has been tested elsewhere.
|
||||||
|
|
||||||
// get all tasks of the service, so we can get the container
|
// get all tasks of the service, so we can get the container
|
||||||
taskResult, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", serviceID),
|
Filters: make(client.Filters).Add("service", serviceID),
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Equal(len(taskResult.Tasks), 1))
|
assert.Check(t, is.Equal(len(taskList.Items), 1))
|
||||||
|
|
||||||
// verify that the container has the capabilities option set
|
// verify that the container has the capabilities option set
|
||||||
ctnr, err := apiClient.ContainerInspect(ctx, taskResult.Tasks[0].Status.ContainerStatus.ContainerID)
|
ctnr, err := apiClient.ContainerInspect(ctx, taskList.Items[0].Status.ContainerStatus.ContainerID)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, ctnr.HostConfig.CapAdd, capAdd)
|
assert.DeepEqual(t, ctnr.HostConfig.CapAdd, capAdd)
|
||||||
assert.DeepEqual(t, ctnr.HostConfig.CapDrop, capDrop)
|
assert.DeepEqual(t, ctnr.HostConfig.CapDrop, capDrop)
|
||||||
|
|
||||||
// verify that the task has the capabilities option set in the task object
|
// verify that the task has the capabilities option set in the task object
|
||||||
assert.DeepEqual(t, taskResult.Tasks[0].Spec.ContainerSpec.CapabilityAdd, capAdd)
|
assert.DeepEqual(t, taskList.Items[0].Spec.ContainerSpec.CapabilityAdd, capAdd)
|
||||||
assert.DeepEqual(t, taskResult.Tasks[0].Spec.ContainerSpec.CapabilityDrop, capDrop)
|
assert.DeepEqual(t, taskList.Items[0].Spec.ContainerSpec.CapabilityDrop, capDrop)
|
||||||
|
|
||||||
// verify that the service also has the capabilities set in the spec.
|
// verify that the service also has the capabilities set in the spec.
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd)
|
assert.DeepEqual(t, result.Service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd)
|
||||||
assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop)
|
assert.DeepEqual(t, result.Service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestInspect(t *testing.T) {
|
|||||||
id := resp.ID
|
id := resp.ID
|
||||||
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, id, instances))
|
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, id, instances))
|
||||||
|
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, id, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, id, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expected := swarmtypes.Service{
|
expected := swarmtypes.Service{
|
||||||
@@ -50,7 +50,7 @@ func TestInspect(t *testing.T) {
|
|||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Check(t, is.DeepEqual(service, expected, cmpServiceOpts()))
|
assert.Check(t, is.DeepEqual(result.Service, expected, cmpServiceOpts()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use helpers from gotest.tools/assert/opt when available
|
// TODO: use helpers from gotest.tools/assert/opt when available
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ func TestReplicatedJob(t *testing.T) {
|
|||||||
swarm.ServiceWithCommand([]string{"true"}),
|
swarm.ServiceWithCommand([]string{"true"}),
|
||||||
)
|
)
|
||||||
|
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(
|
result, err := apiClient.ServiceInspect(
|
||||||
ctx, id, client.ServiceInspectOptions{},
|
ctx, id, client.ServiceInspectOptions{},
|
||||||
)
|
)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, service), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, result.Service), swarm.ServicePoll)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUpdateReplicatedJob tests that a job can be updated, and that it runs with the
|
// TestUpdateReplicatedJob tests that a job can be updated, and that it runs with the
|
||||||
@@ -107,33 +107,33 @@ func TestUpdateReplicatedJob(t *testing.T) {
|
|||||||
swarm.ServiceWithCommand([]string{"true"}),
|
swarm.ServiceWithCommand([]string{"true"}),
|
||||||
)
|
)
|
||||||
|
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(
|
result, err := apiClient.ServiceInspect(
|
||||||
ctx, id, client.ServiceInspectOptions{},
|
ctx, id, client.ServiceInspectOptions{},
|
||||||
)
|
)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
// wait for the job to completed
|
// wait for the job to completed
|
||||||
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, service), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, result.Service), swarm.ServicePoll)
|
||||||
|
|
||||||
// update the job.
|
// update the job.
|
||||||
spec := service.Spec
|
spec := result.Service.Spec
|
||||||
spec.TaskTemplate.ForceUpdate++
|
spec.TaskTemplate.ForceUpdate++
|
||||||
|
|
||||||
_, err = apiClient.ServiceUpdate(
|
_, err = apiClient.ServiceUpdate(
|
||||||
ctx, id, service.Version, spec, client.ServiceUpdateOptions{},
|
ctx, id, result.Service.Version, spec, client.ServiceUpdateOptions{},
|
||||||
)
|
)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
service2, _, err := apiClient.ServiceInspectWithRaw(
|
result2, err := apiClient.ServiceInspect(
|
||||||
ctx, id, client.ServiceInspectOptions{},
|
ctx, id, client.ServiceInspectOptions{},
|
||||||
)
|
)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
// assert that the job iteration has increased
|
// assert that the job iteration has increased
|
||||||
assert.Assert(t,
|
assert.Assert(t,
|
||||||
service.JobStatus.JobIteration.Index < service2.JobStatus.JobIteration.Index,
|
result.Service.JobStatus.JobIteration.Index < result2.Service.JobStatus.JobIteration.Index,
|
||||||
)
|
)
|
||||||
|
|
||||||
// now wait for the service to complete a second time.
|
// now wait for the service to complete a second time.
|
||||||
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, service2), swarm.ServicePoll)
|
poll.WaitOn(t, swarm.JobComplete(ctx, apiClient, result2.Service), swarm.ServicePoll)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ func TestServiceListWithStatuses(t *testing.T) {
|
|||||||
// serviceContainerCount function does not do. instead, we'll use a
|
// serviceContainerCount function does not do. instead, we'll use a
|
||||||
// bespoke closure right here.
|
// bespoke closure right here.
|
||||||
poll.WaitOn(t, func(log poll.LogT) poll.Result {
|
poll.WaitOn(t, func(log poll.LogT) poll.Result {
|
||||||
taskResult, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", id),
|
Filters: make(client.Filters).Add("service", id),
|
||||||
})
|
})
|
||||||
|
|
||||||
running := 0
|
running := 0
|
||||||
for _, task := range taskResult.Tasks {
|
for _, task := range taskList.Items {
|
||||||
if task.Status.State == swarmtypes.TaskStateRunning {
|
if task.Status.State == swarmtypes.TaskStateRunning {
|
||||||
running++
|
running++
|
||||||
}
|
}
|
||||||
@@ -71,25 +71,25 @@ func TestServiceListWithStatuses(t *testing.T) {
|
|||||||
default:
|
default:
|
||||||
return poll.Continue(
|
return poll.Continue(
|
||||||
"running task count %d (%d total), waiting for %d",
|
"running task count %d (%d total), waiting for %d",
|
||||||
running, len(taskResult.Tasks), i+1,
|
running, len(taskList.Items), i+1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, let's do the list operation with no status arg set.
|
// now, let's do the list operation with no status arg set.
|
||||||
resp, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
result, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Len(resp, serviceCount))
|
assert.Check(t, is.Len(result.Services, serviceCount))
|
||||||
for _, service := range resp {
|
for _, service := range result.Services {
|
||||||
assert.Check(t, is.Nil(service.ServiceStatus))
|
assert.Check(t, is.Nil(service.ServiceStatus))
|
||||||
}
|
}
|
||||||
|
|
||||||
// now try again, but with Status: true. This time, we should have statuses
|
// now try again, but with Status: true. This time, we should have statuses
|
||||||
resp, err = apiClient.ServiceList(ctx, client.ServiceListOptions{Status: true})
|
result, err = apiClient.ServiceList(ctx, client.ServiceListOptions{Status: true})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Check(t, is.Len(resp, serviceCount))
|
assert.Check(t, is.Len(result.Services, serviceCount))
|
||||||
for _, service := range resp {
|
for _, service := range result.Services {
|
||||||
replicas := *service.Spec.Mode.Replicated.Replicas
|
replicas := *service.Spec.Mode.Replicated.Replicas
|
||||||
|
|
||||||
assert.Assert(t, service.ServiceStatus != nil)
|
assert.Assert(t, service.ServiceStatus != nil)
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func TestSwarmScopedNetFromConfig(t *testing.T) {
|
|||||||
swarm.ServiceWithNetwork(swarmNetName),
|
swarm.ServiceWithNetwork(swarmNetName),
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := c.ServiceRemove(ctx, serviceID)
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ func TestDockerIngressChainPosition(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := c.ServiceRemove(ctx, serviceID)
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ func TestRestoreIngressRulesOnFirewalldReload(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := c.ServiceRemove(ctx, serviceID)
|
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func TestServiceUpdateLabel(t *testing.T) {
|
|||||||
service = getService(ctx, t, apiClient, serviceID)
|
service = getService(ctx, t, apiClient, serviceID)
|
||||||
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
|
assert.Check(t, is.DeepEqual(service.Spec.Labels, map[string]string{"foo": "bar"}))
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ func TestServiceUpdateSecrets(t *testing.T) {
|
|||||||
service = getService(ctx, t, apiClient, serviceID)
|
service = getService(ctx, t, apiClient, serviceID)
|
||||||
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets)))
|
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Secrets)))
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ func TestServiceUpdateConfigs(t *testing.T) {
|
|||||||
service = getService(ctx, t, apiClient, serviceID)
|
service = getService(ctx, t, apiClient, serviceID)
|
||||||
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs)))
|
assert.Check(t, is.Equal(0, len(service.Spec.TaskTemplate.ContainerSpec.Configs)))
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ func TestServiceUpdateNetwork(t *testing.T) {
|
|||||||
err = apiClient.NetworkRemove(ctx, overlayID)
|
err = apiClient.NetworkRemove(ctx, overlayID)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,19 +319,19 @@ func TestServiceUpdatePidsLimit(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
err := apiClient.ServiceRemove(ctx, serviceID)
|
_, err := apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) container.InspectResponse {
|
func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) container.InspectResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
taskResult, err := cli.TaskList(ctx, client.TaskListOptions{
|
taskList, err := cli.TaskList(ctx, client.TaskListOptions{
|
||||||
Filters: make(client.Filters).Add("service", serviceID).Add("desired-state", "running"),
|
Filters: make(client.Filters).Add("service", serviceID).Add("desired-state", "running"),
|
||||||
})
|
})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Assert(t, len(taskResult.Tasks) > 0)
|
assert.Assert(t, len(taskList.Items) > 0)
|
||||||
|
|
||||||
ctr, err := cli.ContainerInspect(ctx, taskResult.Tasks[0].Status.ContainerStatus.ContainerID)
|
ctr, err := cli.ContainerInspect(ctx, taskList.Items[0].Status.ContainerStatus.ContainerID)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, ctr.State.Running, true)
|
assert.Equal(t, ctr.State.Running, true)
|
||||||
return ctr
|
return ctr
|
||||||
@@ -339,17 +339,19 @@ func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APICl
|
|||||||
|
|
||||||
func getService(ctx context.Context, t *testing.T, apiClient client.ServiceAPIClient, serviceID string) swarmtypes.Service {
|
func getService(ctx context.Context, t *testing.T, apiClient client.ServiceAPIClient, serviceID string) swarmtypes.Service {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return service
|
return result.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceIsUpdated(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
func serviceIsUpdated(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
switch {
|
if err != nil {
|
||||||
case err != nil:
|
|
||||||
return poll.Error(err)
|
return poll.Error(err)
|
||||||
|
}
|
||||||
|
service := result.Service
|
||||||
|
switch {
|
||||||
case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
|
case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
default:
|
default:
|
||||||
@@ -363,11 +365,11 @@ func serviceIsUpdated(ctx context.Context, apiClient client.ServiceAPIClient, se
|
|||||||
|
|
||||||
func serviceSpecIsUpdated(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result {
|
func serviceSpecIsUpdated(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string, serviceOldVersion uint64) func(log poll.LogT) poll.Result {
|
||||||
return func(log poll.LogT) poll.Result {
|
return func(log poll.LogT) poll.Result {
|
||||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
return poll.Error(err)
|
return poll.Error(err)
|
||||||
case service.Version.Index > serviceOldVersion:
|
case result.Service.Version.Index > serviceOldVersion:
|
||||||
return poll.Success()
|
return poll.Success()
|
||||||
default:
|
default:
|
||||||
return poll.Continue("waiting for service %s to be updated", serviceID)
|
return poll.Continue("waiting for service %s to be updated", serviceID)
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ func (d *Daemon) GetService(ctx context.Context, t testing.TB, id string) *swarm
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
service, _, err := cli.ServiceInspectWithRaw(ctx, id, client.ServiceInspectOptions{})
|
res, err := cli.ServiceInspect(ctx, id, client.ServiceInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return &service
|
return &res.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetServiceTasks returns the swarm tasks for the specified service
|
// GetServiceTasks returns the swarm tasks for the specified service
|
||||||
@@ -70,9 +70,9 @@ func (d *Daemon) GetServiceTasksWithFilters(ctx context.Context, t testing.TB, s
|
|||||||
Filters: filterArgs,
|
Filters: filterArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := cli.TaskList(ctx, options)
|
taskList, err := cli.TaskList(ctx, options)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return result.Tasks
|
return taskList.Items
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateService updates a swarm service with the specified service constructor
|
// UpdateService updates a swarm service with the specified service constructor
|
||||||
@@ -95,7 +95,7 @@ func (d *Daemon) RemoveService(ctx context.Context, t testing.TB, id string) {
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
err := cli.ServiceRemove(ctx, id)
|
_, err := cli.ServiceRemove(ctx, id, client.ServiceRemoveOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,9 +105,9 @@ func (d *Daemon) ListServices(ctx context.Context, t testing.TB) []swarm.Service
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
services, err := cli.ServiceList(ctx, client.ServiceListOptions{})
|
res, err := cli.ServiceList(ctx, client.ServiceListOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return services
|
return res.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTask returns the swarm task identified by the specified id
|
// GetTask returns the swarm task identified by the specified id
|
||||||
@@ -116,7 +116,7 @@ func (d *Daemon) GetTask(ctx context.Context, t testing.TB, id string) swarm.Tas
|
|||||||
cli := d.NewClientT(t)
|
cli := d.NewClientT(t)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
result, err := cli.TaskInspect(ctx, id)
|
result, err := cli.TaskInspect(ctx, id, client.TaskInspectOptions{})
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
return result.Task
|
return result.Task
|
||||||
}
|
}
|
||||||
|
|||||||
16
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
16
vendor/github.com/moby/moby/client/client_interfaces.go
generated
vendored
@@ -160,14 +160,14 @@ type PluginAPIClient interface {
|
|||||||
|
|
||||||
// ServiceAPIClient defines API client methods for the services
|
// ServiceAPIClient defines API client methods for the services
|
||||||
type ServiceAPIClient interface {
|
type ServiceAPIClient interface {
|
||||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error)
|
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error)
|
||||||
ServiceInspectWithRaw(ctx context.Context, serviceID string, options ServiceInspectOptions) (swarm.Service, []byte, error)
|
ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error)
|
||||||
ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error)
|
ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, error)
|
||||||
ServiceRemove(ctx context.Context, serviceID string) error
|
ServiceRemove(ctx context.Context, serviceID string, options ServiceRemoveOptions) (ServiceRemoveResult, error)
|
||||||
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error)
|
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (ServiceUpdateResult, error)
|
||||||
ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error)
|
||||||
TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error)
|
||||||
TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error)
|
TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, error)
|
||||||
TaskList(ctx context.Context, options TaskListOptions) (TaskListResult, error)
|
TaskList(ctx context.Context, options TaskListOptions) (TaskListResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
50
vendor/github.com/moby/moby/client/service_create.go
generated
vendored
50
vendor/github.com/moby/moby/client/service_create.go
generated
vendored
@@ -14,35 +14,59 @@ import (
|
|||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceCreate creates a new service.
|
// ServiceCreateOptions contains the options to use when creating a service.
|
||||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error) {
|
type ServiceCreateOptions struct {
|
||||||
var response swarm.ServiceCreateResponse
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreateResult represents the result of creating a service.
|
||||||
|
type ServiceCreateResult struct {
|
||||||
|
// ID is the ID of the created service.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Warnings is a list of warnings that occurred during service creation.
|
||||||
|
Warnings []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceCreate creates a new service.
|
||||||
|
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error) {
|
||||||
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
// Make sure containerSpec is not nil when no runtime is set or the runtime is set to container
|
||||||
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
||||||
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateServiceSpec(service); err != nil {
|
if err := validateServiceSpec(service); err != nil {
|
||||||
return response, err
|
return ServiceCreateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that the image is tagged
|
// ensure that the image is tagged
|
||||||
var resolveWarning string
|
var warnings []string
|
||||||
switch {
|
switch {
|
||||||
case service.TaskTemplate.ContainerSpec != nil:
|
case service.TaskTemplate.ContainerSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
case service.TaskTemplate.PluginSpec != nil:
|
case service.TaskTemplate.PluginSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,15 +77,17 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
|
|||||||
resp, err := cli.post(ctx, "/services/create", nil, service, headers)
|
resp, err := cli.post(ctx, "/services/create", nil, service, headers)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response, err
|
return ServiceCreateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var response swarm.ServiceCreateResponse
|
||||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
if resolveWarning != "" {
|
warnings = append(warnings, response.Warnings...)
|
||||||
response.Warnings = append(response.Warnings, resolveWarning)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, err
|
return ServiceCreateResult{
|
||||||
|
ID: response.ID,
|
||||||
|
Warnings: warnings,
|
||||||
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
||||||
|
|||||||
36
vendor/github.com/moby/moby/client/service_inspect.go
generated
vendored
36
vendor/github.com/moby/moby/client/service_inspect.go
generated
vendored
@@ -1,38 +1,40 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceInspectWithRaw returns the service information and the raw data.
|
// ServiceInspectOptions holds parameters related to the service inspect operation.
|
||||||
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts ServiceInspectOptions) (swarm.Service, []byte, error) {
|
type ServiceInspectOptions struct {
|
||||||
|
InsertDefaults bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspectResult represents the result of a service inspect operation.
|
||||||
|
type ServiceInspectResult struct {
|
||||||
|
Service swarm.Service
|
||||||
|
Raw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceInspect retrieves detailed information about a specific service by its ID.
|
||||||
|
func (cli *Client) ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.Service{}, nil, err
|
return ServiceInspectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("insertDefaults", fmt.Sprintf("%v", opts.InsertDefaults))
|
query.Set("insertDefaults", fmt.Sprintf("%v", options.InsertDefaults))
|
||||||
resp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
resp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.Service{}, nil, err
|
return ServiceInspectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
var out ServiceInspectResult
|
||||||
if err != nil {
|
out.Raw, err = decodeWithRaw(resp, &out.Service)
|
||||||
return swarm.Service{}, nil, err
|
return out, err
|
||||||
}
|
|
||||||
|
|
||||||
var response swarm.Service
|
|
||||||
rdr := bytes.NewReader(body)
|
|
||||||
err = json.NewDecoder(rdr).Decode(&response)
|
|
||||||
return response, body, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
vendor/github.com/moby/moby/client/service_list.go
generated
vendored
20
vendor/github.com/moby/moby/client/service_list.go
generated
vendored
@@ -8,8 +8,22 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceListOptions holds parameters to list services with.
|
||||||
|
type ServiceListOptions struct {
|
||||||
|
Filters Filters
|
||||||
|
|
||||||
|
// Status indicates whether the server should include the service task
|
||||||
|
// count of running and desired tasks.
|
||||||
|
Status bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceListResult represents the result of a service list operation.
|
||||||
|
type ServiceListResult struct {
|
||||||
|
Services []swarm.Service
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceList returns the list of services.
|
// ServiceList returns the list of services.
|
||||||
func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error) {
|
func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
||||||
options.Filters.updateURLValues(query)
|
options.Filters.updateURLValues(query)
|
||||||
@@ -21,10 +35,10 @@ func (cli *Client) ServiceList(ctx context.Context, options ServiceListOptions)
|
|||||||
resp, err := cli.get(ctx, "/services", query, nil)
|
resp, err := cli.get(ctx, "/services", query, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceListResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var services []swarm.Service
|
var services []swarm.Service
|
||||||
err = json.NewDecoder(resp.Body).Decode(&services)
|
err = json.NewDecoder(resp.Body).Decode(&services)
|
||||||
return services, err
|
return ServiceListResult{Services: services}, err
|
||||||
}
|
}
|
||||||
|
|||||||
58
vendor/github.com/moby/moby/client/service_logs.go
generated
vendored
58
vendor/github.com/moby/moby/client/service_logs.go
generated
vendored
@@ -5,17 +5,37 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/moby/moby/client/internal/timestamp"
|
"github.com/moby/moby/client/internal/timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceLogs returns the logs generated by a service in an [io.ReadCloser].
|
// ServiceLogsOptions holds parameters to filter logs with.
|
||||||
|
type ServiceLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceLogsResult holds the result of a service logs operation.
|
||||||
|
// It implements [io.ReadCloser].
|
||||||
// It's up to the caller to close the stream.
|
// It's up to the caller to close the stream.
|
||||||
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error) {
|
type ServiceLogsResult struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
close func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceLogs returns the logs generated by a service in an [ServiceLogsResult].
|
||||||
|
func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceLogsResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
@@ -30,7 +50,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
|||||||
if options.Since != "" {
|
if options.Since != "" {
|
||||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`invalid value for "since": %w`, err)
|
return ServiceLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err)
|
||||||
}
|
}
|
||||||
query.Set("since", ts)
|
query.Set("since", ts)
|
||||||
}
|
}
|
||||||
@@ -50,7 +70,33 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
|||||||
|
|
||||||
resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil)
|
resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return ServiceLogsResult{}, err
|
||||||
}
|
}
|
||||||
return resp.Body, nil
|
return newServiceLogsResult(resp.Body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServiceLogsResult(rc io.ReadCloser) ServiceLogsResult {
|
||||||
|
if rc == nil {
|
||||||
|
panic("nil io.ReadCloser")
|
||||||
|
}
|
||||||
|
return ServiceLogsResult{
|
||||||
|
rc: rc,
|
||||||
|
close: sync.OnceValue(rc.Close),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r ServiceLogsResult) Read(p []byte) (n int, err error) {
|
||||||
|
if r.rc == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.rc.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r ServiceLogsResult) Close() error {
|
||||||
|
if r.close == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.close()
|
||||||
}
|
}
|
||||||
|
|||||||
16
vendor/github.com/moby/moby/client/service_remove.go
generated
vendored
16
vendor/github.com/moby/moby/client/service_remove.go
generated
vendored
@@ -2,14 +2,24 @@ package client
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
|
// ServiceRemoveOptions contains options for removing a service.
|
||||||
|
type ServiceRemoveOptions struct {
|
||||||
|
// No options currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceRemoveResult contains the result of removing a service.
|
||||||
|
type ServiceRemoveResult struct {
|
||||||
|
// No fields currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceRemove kills and removes a service.
|
// ServiceRemove kills and removes a service.
|
||||||
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string, options ServiceRemoveOptions) (ServiceRemoveResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ServiceRemoveResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
return err
|
return ServiceRemoveResult{}, err
|
||||||
}
|
}
|
||||||
|
|||||||
59
vendor/github.com/moby/moby/client/service_update.go
generated
vendored
59
vendor/github.com/moby/moby/client/service_update.go
generated
vendored
@@ -10,18 +10,54 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceUpdateOptions contains the options to be used for updating services.
|
||||||
|
type ServiceUpdateOptions struct {
|
||||||
|
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
||||||
|
// use when updating the service.
|
||||||
|
//
|
||||||
|
// This field follows the format of the X-Registry-Auth header.
|
||||||
|
EncodedRegistryAuth string
|
||||||
|
|
||||||
|
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
||||||
|
// into this field. While it does open API users up to racy writes, most
|
||||||
|
// users may not need that level of consistency in practice.
|
||||||
|
|
||||||
|
// RegistryAuthFrom specifies where to find the registry authorization
|
||||||
|
// credentials if they are not given in EncodedRegistryAuth. Valid
|
||||||
|
// values are "spec" and "previous-spec".
|
||||||
|
RegistryAuthFrom string
|
||||||
|
|
||||||
|
// Rollback indicates whether a server-side rollback should be
|
||||||
|
// performed. When this is set, the provided spec will be ignored.
|
||||||
|
// The valid values are "previous" and "none". An empty value is the
|
||||||
|
// same as "none".
|
||||||
|
Rollback string
|
||||||
|
|
||||||
|
// QueryRegistry indicates whether the service update requires
|
||||||
|
// contacting a registry. A registry may be contacted to retrieve
|
||||||
|
// the image digest and manifest, which in turn can be used to update
|
||||||
|
// platform or other information about the service.
|
||||||
|
QueryRegistry bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceUpdateResult represents the result of a service update.
|
||||||
|
type ServiceUpdateResult struct {
|
||||||
|
// Warnings contains any warnings that occurred during the update.
|
||||||
|
Warnings []string
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceUpdate updates a Service. The version number is required to avoid
|
// ServiceUpdate updates a Service. The version number is required to avoid
|
||||||
// conflicting writes. It must be the value as set *before* the update.
|
// conflicting writes. It must be the value as set *before* the update.
|
||||||
// You can find this value in the [swarm.Service.Meta] field, which can
|
// You can find this value in the [swarm.Service.Meta] field, which can
|
||||||
// be found using [Client.ServiceInspectWithRaw].
|
// be found using [Client.ServiceInspectWithRaw].
|
||||||
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) {
|
func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (ServiceUpdateResult, error) {
|
||||||
serviceID, err := trimID("service", serviceID)
|
serviceID, err := trimID("service", serviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateServiceSpec(service); err != nil {
|
if err := validateServiceSpec(service); err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
@@ -36,21 +72,23 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
query.Set("version", version.String())
|
query.Set("version", version.String())
|
||||||
|
|
||||||
// ensure that the image is tagged
|
// ensure that the image is tagged
|
||||||
var resolveWarning string
|
var warnings []string
|
||||||
switch {
|
switch {
|
||||||
case service.TaskTemplate.ContainerSpec != nil:
|
case service.TaskTemplate.ContainerSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolveContainerSpecImage(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
case service.TaskTemplate.PluginSpec != nil:
|
case service.TaskTemplate.PluginSpec != nil:
|
||||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||||
}
|
}
|
||||||
if options.QueryRegistry {
|
if options.QueryRegistry {
|
||||||
resolveWarning = resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
resolveWarning := resolvePluginSpecRemote(ctx, cli, &service.TaskTemplate, options.EncodedRegistryAuth)
|
||||||
|
warnings = append(warnings, resolveWarning)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +99,11 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
|||||||
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
resp, err := cli.post(ctx, "/services/"+serviceID+"/update", query, service, headers)
|
||||||
defer ensureReaderClosed(resp)
|
defer ensureReaderClosed(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return swarm.ServiceUpdateResponse{}, err
|
return ServiceUpdateResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var response swarm.ServiceUpdateResponse
|
var response swarm.ServiceUpdateResponse
|
||||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
if resolveWarning != "" {
|
warnings = append(warnings, response.Warnings...)
|
||||||
response.Warnings = append(response.Warnings, resolveWarning)
|
return ServiceUpdateResult{Warnings: warnings}, err
|
||||||
}
|
|
||||||
|
|
||||||
return response, err
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
vendor/github.com/moby/moby/client/swarm_join.go
generated
vendored
4
vendor/github.com/moby/moby/client/swarm_join.go
generated
vendored
@@ -17,7 +17,9 @@ type SwarmJoinOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwarmJoinResult contains the result of joining a swarm.
|
// SwarmJoinResult contains the result of joining a swarm.
|
||||||
type SwarmJoinResult struct{}
|
type SwarmJoinResult struct {
|
||||||
|
// No fields currently; placeholder for future use
|
||||||
|
}
|
||||||
|
|
||||||
// SwarmJoin joins the swarm.
|
// SwarmJoin joins the swarm.
|
||||||
func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) {
|
func (cli *Client) SwarmJoin(ctx context.Context, options SwarmJoinOptions) (SwarmJoinResult, error) {
|
||||||
|
|||||||
16
vendor/github.com/moby/moby/client/swarm_service_create_opts.go
generated
vendored
16
vendor/github.com/moby/moby/client/swarm_service_create_opts.go
generated
vendored
@@ -1,16 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceCreateOptions contains the options to use when creating a service.
|
|
||||||
type ServiceCreateOptions struct {
|
|
||||||
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
|
||||||
// use when updating the service.
|
|
||||||
//
|
|
||||||
// This field follows the format of the X-Registry-Auth header.
|
|
||||||
EncodedRegistryAuth string
|
|
||||||
|
|
||||||
// QueryRegistry indicates whether the service update requires
|
|
||||||
// contacting a registry. A registry may be contacted to retrieve
|
|
||||||
// the image digest and manifest, which in turn can be used to update
|
|
||||||
// platform or other information about the service.
|
|
||||||
QueryRegistry bool
|
|
||||||
}
|
|
||||||
7
vendor/github.com/moby/moby/client/swarm_service_inspect_opts.go
generated
vendored
7
vendor/github.com/moby/moby/client/swarm_service_inspect_opts.go
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceInspectOptions holds parameters related to the "service inspect"
|
|
||||||
// operation.
|
|
||||||
type ServiceInspectOptions struct {
|
|
||||||
InsertDefaults bool
|
|
||||||
}
|
|
||||||
10
vendor/github.com/moby/moby/client/swarm_service_list_opts.go
generated
vendored
10
vendor/github.com/moby/moby/client/swarm_service_list_opts.go
generated
vendored
@@ -1,10 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceListOptions holds parameters to list services with.
|
|
||||||
type ServiceListOptions struct {
|
|
||||||
Filters Filters
|
|
||||||
|
|
||||||
// Status indicates whether the server should include the service task
|
|
||||||
// count of running and desired tasks.
|
|
||||||
Status bool
|
|
||||||
}
|
|
||||||
31
vendor/github.com/moby/moby/client/swarm_service_update_opts.go
generated
vendored
31
vendor/github.com/moby/moby/client/swarm_service_update_opts.go
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
// ServiceUpdateOptions contains the options to be used for updating services.
|
|
||||||
type ServiceUpdateOptions struct {
|
|
||||||
// EncodedRegistryAuth is the encoded registry authorization credentials to
|
|
||||||
// use when updating the service.
|
|
||||||
//
|
|
||||||
// This field follows the format of the X-Registry-Auth header.
|
|
||||||
EncodedRegistryAuth string
|
|
||||||
|
|
||||||
// TODO(stevvooe): Consider moving the version parameter of ServiceUpdate
|
|
||||||
// into this field. While it does open API users up to racy writes, most
|
|
||||||
// users may not need that level of consistency in practice.
|
|
||||||
|
|
||||||
// RegistryAuthFrom specifies where to find the registry authorization
|
|
||||||
// credentials if they are not given in EncodedRegistryAuth. Valid
|
|
||||||
// values are "spec" and "previous-spec".
|
|
||||||
RegistryAuthFrom string
|
|
||||||
|
|
||||||
// Rollback indicates whether a server-side rollback should be
|
|
||||||
// performed. When this is set, the provided spec will be ignored.
|
|
||||||
// The valid values are "previous" and "none". An empty value is the
|
|
||||||
// same as "none".
|
|
||||||
Rollback string
|
|
||||||
|
|
||||||
// QueryRegistry indicates whether the service update requires
|
|
||||||
// contacting a registry. A registry may be contacted to retrieve
|
|
||||||
// the image digest and manifest, which in turn can be used to update
|
|
||||||
// platform or other information about the service.
|
|
||||||
QueryRegistry bool
|
|
||||||
}
|
|
||||||
7
vendor/github.com/moby/moby/client/task_inspect.go
generated
vendored
7
vendor/github.com/moby/moby/client/task_inspect.go
generated
vendored
@@ -6,6 +6,11 @@ import (
|
|||||||
"github.com/moby/moby/api/types/swarm"
|
"github.com/moby/moby/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TaskInspectOptions contains options for inspecting a task.
|
||||||
|
type TaskInspectOptions struct {
|
||||||
|
// Currently no options are defined.
|
||||||
|
}
|
||||||
|
|
||||||
// TaskInspectResult contains the result of a task inspection.
|
// TaskInspectResult contains the result of a task inspection.
|
||||||
type TaskInspectResult struct {
|
type TaskInspectResult struct {
|
||||||
Task swarm.Task
|
Task swarm.Task
|
||||||
@@ -13,7 +18,7 @@ type TaskInspectResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TaskInspect returns the task information and its raw representation.
|
// TaskInspect returns the task information and its raw representation.
|
||||||
func (cli *Client) TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error) {
|
func (cli *Client) TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, error) {
|
||||||
taskID, err := trimID("task", taskID)
|
taskID, err := trimID("task", taskID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TaskInspectResult{}, err
|
return TaskInspectResult{}, err
|
||||||
|
|||||||
4
vendor/github.com/moby/moby/client/task_list.go
generated
vendored
4
vendor/github.com/moby/moby/client/task_list.go
generated
vendored
@@ -15,7 +15,7 @@ type TaskListOptions struct {
|
|||||||
|
|
||||||
// TaskListResult contains the result of a task list operation.
|
// TaskListResult contains the result of a task list operation.
|
||||||
type TaskListResult struct {
|
type TaskListResult struct {
|
||||||
Tasks []swarm.Task
|
Items []swarm.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskList returns the list of tasks.
|
// TaskList returns the list of tasks.
|
||||||
@@ -32,5 +32,5 @@ func (cli *Client) TaskList(ctx context.Context, options TaskListOptions) (TaskL
|
|||||||
|
|
||||||
var tasks []swarm.Task
|
var tasks []swarm.Task
|
||||||
err = json.NewDecoder(resp.Body).Decode(&tasks)
|
err = json.NewDecoder(resp.Body).Decode(&tasks)
|
||||||
return TaskListResult{Tasks: tasks}, err
|
return TaskListResult{Items: tasks}, err
|
||||||
}
|
}
|
||||||
|
|||||||
56
vendor/github.com/moby/moby/client/task_logs.go
generated
vendored
56
vendor/github.com/moby/moby/client/task_logs.go
generated
vendored
@@ -4,14 +4,34 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/moby/moby/client/internal/timestamp"
|
"github.com/moby/moby/client/internal/timestamp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskLogs returns the logs generated by a task in an [io.ReadCloser].
|
// TaskLogsOptions holds parameters to filter logs with.
|
||||||
|
type TaskLogsOptions struct {
|
||||||
|
ShowStdout bool
|
||||||
|
ShowStderr bool
|
||||||
|
Since string
|
||||||
|
Until string
|
||||||
|
Timestamps bool
|
||||||
|
Follow bool
|
||||||
|
Tail string
|
||||||
|
Details bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskLogsResult holds the result of a task logs operation.
|
||||||
|
// It implements [io.ReadCloser].
|
||||||
|
type TaskLogsResult struct {
|
||||||
|
rc io.ReadCloser
|
||||||
|
close func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TaskLogs returns the logs generated by a task.
|
||||||
// It's up to the caller to close the stream.
|
// It's up to the caller to close the stream.
|
||||||
func (cli *Client) TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error) {
|
func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
if options.ShowStdout {
|
if options.ShowStdout {
|
||||||
query.Set("stdout", "1")
|
query.Set("stdout", "1")
|
||||||
@@ -24,7 +44,7 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
|||||||
if options.Since != "" {
|
if options.Since != "" {
|
||||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return TaskLogsResult{}, err
|
||||||
}
|
}
|
||||||
query.Set("since", ts)
|
query.Set("since", ts)
|
||||||
}
|
}
|
||||||
@@ -44,7 +64,33 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
|||||||
|
|
||||||
resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil)
|
resp, err := cli.get(ctx, "/tasks/"+taskID+"/logs", query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return TaskLogsResult{}, err
|
||||||
}
|
}
|
||||||
return resp.Body, nil
|
return newTaskLogsResult(resp.Body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTaskLogsResult(rc io.ReadCloser) TaskLogsResult {
|
||||||
|
if rc == nil {
|
||||||
|
panic("nil io.ReadCloser")
|
||||||
|
}
|
||||||
|
return TaskLogsResult{
|
||||||
|
rc: rc,
|
||||||
|
close: sync.OnceValue(rc.Close),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r TaskLogsResult) Read(p []byte) (n int, err error) {
|
||||||
|
if r.rc == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.rc.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements [io.ReadCloser] for LogsResult.
|
||||||
|
func (r TaskLogsResult) Close() error {
|
||||||
|
if r.close == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return r.close()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user