mirror of
https://github.com/moby/moby.git
synced 2026-01-11 02:31:44 +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
|
||||
type ServiceAPIClient interface {
|
||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error)
|
||||
ServiceInspectWithRaw(ctx context.Context, serviceID string, options ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||
ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error)
|
||||
ServiceRemove(ctx context.Context, serviceID string) error
|
||||
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error)
|
||||
ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
||||
TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
||||
TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error)
|
||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error)
|
||||
ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error)
|
||||
ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, 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) (ServiceUpdateResult, error)
|
||||
ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error)
|
||||
TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error)
|
||||
TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, error)
|
||||
TaskList(ctx context.Context, options TaskListOptions) (TaskListResult, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,35 +14,59 @@ import (
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// ServiceCreate creates a new service.
|
||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error) {
|
||||
var response swarm.ServiceCreateResponse
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
||||
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||
}
|
||||
|
||||
if err := validateServiceSpec(service); err != nil {
|
||||
return response, err
|
||||
return ServiceCreateResult{}, err
|
||||
}
|
||||
|
||||
// ensure that the image is tagged
|
||||
var resolveWarning string
|
||||
var warnings []string
|
||||
switch {
|
||||
case service.TaskTemplate.ContainerSpec != nil:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
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:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return response, err
|
||||
return ServiceCreateResult{}, err
|
||||
}
|
||||
|
||||
var response swarm.ServiceCreateResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if resolveWarning != "" {
|
||||
response.Warnings = append(response.Warnings, resolveWarning)
|
||||
}
|
||||
warnings = append(warnings, response.Warnings...)
|
||||
|
||||
return response, err
|
||||
return ServiceCreateResult{
|
||||
ID: response.ID,
|
||||
Warnings: warnings,
|
||||
}, err
|
||||
}
|
||||
|
||||
func resolveContainerSpecImage(ctx context.Context, cli DistributionAPIClient, taskSpec *swarm.TaskSpec, encodedAuth string) string {
|
||||
|
||||
@@ -1,38 +1,40 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/moby/moby/api/types/swarm"
|
||||
)
|
||||
|
||||
// ServiceInspectWithRaw returns the service information and the raw data.
|
||||
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
// ServiceInspectOptions holds parameters related to the service inspect operation.
|
||||
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)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
return ServiceInspectResult{}, err
|
||||
}
|
||||
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
return ServiceInspectResult{}, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
}
|
||||
|
||||
var response swarm.Service
|
||||
rdr := bytes.NewReader(body)
|
||||
err = json.NewDecoder(rdr).Decode(&response)
|
||||
return response, body, err
|
||||
var out ServiceInspectResult
|
||||
out.Raw, err = decodeWithRaw(resp, &out.Service)
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestServiceInspectError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestServiceInspectServiceNotFound(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusNotFound, "Server error")))
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ func TestServiceInspectWithEmptyID(t *testing.T) {
|
||||
return nil, errors.New("should not make request")
|
||||
}))
|
||||
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.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.ErrorContains(err, "value is empty"))
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func TestServiceInspect(t *testing.T) {
|
||||
}))
|
||||
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.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"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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{}
|
||||
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ServiceListResult{}, err
|
||||
}
|
||||
|
||||
var services []swarm.Service
|
||||
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)
|
||||
|
||||
services, err := client.ServiceList(context.Background(), listCase.options)
|
||||
list, err := client.ServiceList(context.Background(), listCase.options)
|
||||
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"
|
||||
"io"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ServiceLogsResult{}, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
@@ -30,7 +50,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
||||
if options.Since != "" {
|
||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||
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)
|
||||
}
|
||||
@@ -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)
|
||||
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) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
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))
|
||||
|
||||
_, err = client.ServiceLogs(context.Background(), "service_id", ContainerLogsOptions{
|
||||
_, err = client.ServiceLogs(context.Background(), "service_id", ServiceLogsOptions{
|
||||
Since: "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.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.ErrorContains(err, "value is empty"))
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func TestServiceLogsError(t *testing.T) {
|
||||
func TestServiceLogs(t *testing.T) {
|
||||
const expectedURL = "/services/service_id/logs"
|
||||
cases := []struct {
|
||||
options ContainerLogsOptions
|
||||
options ServiceLogsOptions
|
||||
expectedQueryParams map[string]string
|
||||
expectedError string
|
||||
}{
|
||||
@@ -50,7 +50,7 @@ func TestServiceLogs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
options: ContainerLogsOptions{
|
||||
options: ServiceLogsOptions{
|
||||
Tail: "any",
|
||||
},
|
||||
expectedQueryParams: map[string]string{
|
||||
@@ -58,7 +58,7 @@ func TestServiceLogs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
options: ContainerLogsOptions{
|
||||
options: ServiceLogsOptions{
|
||||
ShowStdout: true,
|
||||
ShowStderr: true,
|
||||
Timestamps: true,
|
||||
@@ -75,7 +75,7 @@ func TestServiceLogs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
options: ContainerLogsOptions{
|
||||
options: ServiceLogsOptions{
|
||||
// timestamp is passed as-is
|
||||
Since: "1136073600.000000001",
|
||||
},
|
||||
@@ -85,7 +85,7 @@ func TestServiceLogs(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
options: ContainerLogsOptions{
|
||||
options: ServiceLogsOptions{
|
||||
// invalid dates are not passed.
|
||||
Since: "invalid value",
|
||||
},
|
||||
@@ -129,7 +129,7 @@ func ExampleClient_ServiceLogs_withTimeout() {
|
||||
defer cancel()
|
||||
|
||||
client, _ := NewClientWithOpts(FromEnv)
|
||||
reader, err := client.ServiceLogs(ctx, "service_id", ContainerLogsOptions{})
|
||||
reader, err := client.ServiceLogs(ctx, "service_id", ServiceLogsOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,24 @@ package client
|
||||
|
||||
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.
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return ServiceRemoveResult{}, err
|
||||
}
|
||||
|
||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||
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")))
|
||||
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))
|
||||
|
||||
err = client.ServiceRemove(context.Background(), "")
|
||||
_, err = client.ServiceRemove(context.Background(), "", ServiceRemoveOptions{})
|
||||
assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
|
||||
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.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")))
|
||||
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.ErrorType(err, cerrdefs.IsNotFound))
|
||||
}
|
||||
@@ -51,6 +51,6 @@ func TestServiceRemove(t *testing.T) {
|
||||
}))
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = client.ServiceRemove(context.Background(), "service_id")
|
||||
_, err = client.ServiceRemove(context.Background(), "service_id", ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -10,18 +10,54 @@ import (
|
||||
"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
|
||||
// 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
|
||||
// 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)
|
||||
if err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
if err := validateServiceSpec(service); err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
@@ -36,21 +72,23 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||
query.Set("version", version.String())
|
||||
|
||||
// ensure that the image is tagged
|
||||
var resolveWarning string
|
||||
var warnings []string
|
||||
switch {
|
||||
case service.TaskTemplate.ContainerSpec != nil:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
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:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
var response swarm.ServiceUpdateResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if resolveWarning != "" {
|
||||
response.Warnings = append(response.Warnings, resolveWarning)
|
||||
}
|
||||
|
||||
return response, err
|
||||
warnings = append(warnings, response.Warnings...)
|
||||
return ServiceUpdateResult{Warnings: warnings}, err
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ type SwarmJoinOptions struct {
|
||||
}
|
||||
|
||||
// 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.
|
||||
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"
|
||||
)
|
||||
|
||||
// TaskInspectOptions contains options for inspecting a task.
|
||||
type TaskInspectOptions struct {
|
||||
// Currently no options are defined.
|
||||
}
|
||||
|
||||
// TaskInspectResult contains the result of a task inspection.
|
||||
type TaskInspectResult struct {
|
||||
Task swarm.Task
|
||||
@@ -13,7 +18,7 @@ type TaskInspectResult struct {
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return TaskInspectResult{}, err
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestTaskInspectError(t *testing.T) {
|
||||
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ func TestTaskInspectWithEmptyID(t *testing.T) {
|
||||
return nil, errors.New("should not make request")
|
||||
}))
|
||||
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.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.ErrorContains(err, "value is empty"))
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func TestTaskInspect(t *testing.T) {
|
||||
}))
|
||||
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.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.
|
||||
type TaskListResult struct {
|
||||
Tasks []swarm.Task
|
||||
Items []swarm.Task
|
||||
}
|
||||
|
||||
// TaskList returns the list of tasks.
|
||||
@@ -32,5 +32,5 @@ func (cli *Client) TaskList(ctx context.Context, options TaskListOptions) (TaskL
|
||||
|
||||
var tasks []swarm.Task
|
||||
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)
|
||||
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"
|
||||
"io"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
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{}
|
||||
if options.ShowStdout {
|
||||
query.Set("stdout", "1")
|
||||
@@ -24,7 +44,7 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
||||
if options.Since != "" {
|
||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return TaskLogsResult{}, err
|
||||
}
|
||||
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)
|
||||
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)
|
||||
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"),
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
result := make(map[string]int)
|
||||
for _, task := range taskResult.Tasks {
|
||||
for _, task := range taskList.Items {
|
||||
for _, network := range task.Spec.Networks {
|
||||
result[network.Target]++
|
||||
}
|
||||
@@ -124,13 +124,13 @@ func (d *Daemon) CheckRunningTaskImages(ctx context.Context) func(t *testing.T)
|
||||
cli := d.NewClientT(t)
|
||||
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"),
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
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 {
|
||||
result[task.Spec.ContainerSpec.Image]++
|
||||
}
|
||||
|
||||
@@ -78,14 +78,14 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *testing.T) {
|
||||
options := client.ServiceInspectOptions{InsertDefaults: true}
|
||||
|
||||
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
||||
resp, _, err := apiClient.ServiceInspectWithRaw(ctx, id, options)
|
||||
out := fmt.Sprintf("%+v", resp)
|
||||
res, err := apiClient.ServiceInspect(ctx, id, options)
|
||||
out := fmt.Sprintf("%+v", res.Service)
|
||||
assert.NilError(c, err)
|
||||
assert.Assert(c, is.Contains(out, "UpdateConfig"))
|
||||
|
||||
// insertDefaults inserts UpdateConfig when service is fetched by ID
|
||||
resp, _, err = apiClient.ServiceInspectWithRaw(ctx, "top", options)
|
||||
out = fmt.Sprintf("%+v", resp)
|
||||
res, err = apiClient.ServiceInspect(ctx, "top", options)
|
||||
out = fmt.Sprintf("%+v", res.Service)
|
||||
assert.NilError(c, err)
|
||||
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 {
|
||||
t.Helper()
|
||||
|
||||
result, err := c.TaskList(ctx, client.TaskListOptions{
|
||||
taskList, err := c.TaskList(ctx, client.TaskListOptions{
|
||||
Filters: make(client.Filters).
|
||||
Add("service", serviceID).
|
||||
Add("desired-state", "running"),
|
||||
})
|
||||
|
||||
assert.NilError(t, err)
|
||||
return result.Tasks
|
||||
return taskList.Items
|
||||
}
|
||||
|
||||
// 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
|
||||
func NoTasksForService(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string) 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),
|
||||
})
|
||||
if err == nil {
|
||||
if len(result.Tasks) == 0 {
|
||||
if len(taskList.Items) == 0 {
|
||||
return poll.Success()
|
||||
}
|
||||
if len(result.Tasks) > 0 {
|
||||
return poll.Continue("task count for service %s at %d waiting for 0", serviceID, len(result.Tasks))
|
||||
if len(taskList.Items) > 0 {
|
||||
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)
|
||||
}
|
||||
@@ -32,14 +32,14 @@ func NoTasksForService(ctx context.Context, apiClient client.ServiceAPIClient, s
|
||||
// NoTasks verifies that all tasks are gone
|
||||
func NoTasks(ctx context.Context, apiClient client.ServiceAPIClient) 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 {
|
||||
case err != nil:
|
||||
return poll.Error(err)
|
||||
case len(result.Tasks) == 0:
|
||||
case len(taskResult.Items) == 0:
|
||||
return poll.Success()
|
||||
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`
|
||||
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 {
|
||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||
Filters: make(client.Filters).Add("service", serviceID),
|
||||
})
|
||||
var running int
|
||||
var taskError string
|
||||
for _, task := range result.Tasks {
|
||||
for _, task := range taskList.Items {
|
||||
switch task.Status.State {
|
||||
case swarmtypes.TaskStateRunning:
|
||||
running++
|
||||
@@ -76,7 +76,7 @@ func RunningTasksCount(ctx context.Context, apiClient client.ServiceAPIClient, s
|
||||
case running == int(instances):
|
||||
return poll.Success()
|
||||
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 := ""
|
||||
|
||||
return func(log poll.LogT) poll.Result {
|
||||
result, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||
taskList, err := apiClient.TaskList(ctx, client.TaskListOptions{
|
||||
Filters: filter,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -110,7 +110,7 @@ func JobComplete(ctx context.Context, apiClient client.ServiceAPIClient, service
|
||||
var runningSlot []int
|
||||
var runningID []string
|
||||
|
||||
for _, task := range result.Tasks {
|
||||
for _, task := range taskList.Items {
|
||||
// make sure the task has the same job iteration
|
||||
if task.JobIteration == nil || task.JobIteration.Index != jobIteration.Index {
|
||||
continue
|
||||
|
||||
@@ -76,7 +76,8 @@ func TestInspectNetwork(t *testing.T) {
|
||||
swarm.ServiceWithNetwork(networkName),
|
||||
)
|
||||
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)
|
||||
}()
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ func TestHostPortMappings(t *testing.T) {
|
||||
{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)
|
||||
|
||||
|
||||
@@ -245,10 +245,10 @@ func TestServiceWithPredefinedNetwork(t *testing.T) {
|
||||
|
||||
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)
|
||||
|
||||
err = c.ServiceRemove(ctx, serviceID)
|
||||
_, err = c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -286,10 +286,10 @@ func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
|
||||
|
||||
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)
|
||||
|
||||
err = c.ServiceRemove(ctx, serviceID)
|
||||
_, err = c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
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 {
|
||||
return func(log poll.LogT) poll.Result {
|
||||
services, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||
result, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||
switch {
|
||||
case err != nil:
|
||||
return poll.Error(err)
|
||||
case len(services) == 0:
|
||||
case len(result.Services) == 0:
|
||||
return poll.Success()
|
||||
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)
|
||||
assert.Equal(t, info.Swarm.Cluster.DataPathPort, datapathPort)
|
||||
err := c.ServiceRemove(ctx, serviceID)
|
||||
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
poll.WaitOn(t, noServices(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)
|
||||
var defaultDataPathPort uint32 = 4789
|
||||
assert.Equal(t, info.Swarm.Cluster.DataPathPort, defaultDataPathPort)
|
||||
err = nc.ServiceRemove(ctx, serviceID)
|
||||
_, err = nc.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
poll.WaitOn(t, noServices(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)
|
||||
|
||||
_, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
_, err := cli.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
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.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, swarm.NoTasks(ctx, cli), swarm.ServicePoll)
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -100,10 +100,10 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
|
||||
serviceID := swarm.CreateService(ctx, t, d, serviceSpec...)
|
||||
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)
|
||||
|
||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
||||
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
|
||||
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...)
|
||||
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)
|
||||
|
||||
// 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...)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
|
||||
|
||||
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",
|
||||
ShowStdout: true,
|
||||
})
|
||||
@@ -238,7 +238,7 @@ func TestCreateServiceSecretFileMode(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
||||
|
||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
||||
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
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))
|
||||
|
||||
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ContainerLogsOptions{
|
||||
body, err := apiClient.ServiceLogs(ctx, serviceID, client.ServiceLogsOptions{
|
||||
Tail: "1",
|
||||
ShowStdout: true,
|
||||
})
|
||||
@@ -297,7 +297,7 @@ func TestCreateServiceConfigFileMode(t *testing.T) {
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Contains(string(content), "-rwxrwxrwx"))
|
||||
|
||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
||||
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
poll.WaitOn(t, swarm.NoTasksForService(ctx, apiClient, serviceID))
|
||||
|
||||
@@ -368,25 +368,25 @@ func TestCreateServiceSysctls(t *testing.T) {
|
||||
// more complex)
|
||||
|
||||
// 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),
|
||||
})
|
||||
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
|
||||
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.DeepEqual(t, ctnr.HostConfig.Sysctls, expectedSysctls)
|
||||
|
||||
// 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.
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
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.
|
||||
|
||||
// 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),
|
||||
})
|
||||
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
|
||||
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.DeepEqual(t, ctnr.HostConfig.CapAdd, capAdd)
|
||||
assert.DeepEqual(t, ctnr.HostConfig.CapDrop, capDrop)
|
||||
|
||||
// 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, taskResult.Tasks[0].Spec.ContainerSpec.CapabilityDrop, capDrop)
|
||||
assert.DeepEqual(t, taskList.Items[0].Spec.ContainerSpec.CapabilityAdd, capAdd)
|
||||
assert.DeepEqual(t, taskList.Items[0].Spec.ContainerSpec.CapabilityDrop, capDrop)
|
||||
|
||||
// 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.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd)
|
||||
assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop)
|
||||
assert.DeepEqual(t, result.Service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd)
|
||||
assert.DeepEqual(t, result.Service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestInspect(t *testing.T) {
|
||||
id := resp.ID
|
||||
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)
|
||||
|
||||
expected := swarmtypes.Service{
|
||||
@@ -50,7 +50,7 @@ func TestInspect(t *testing.T) {
|
||||
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
|
||||
|
||||
@@ -74,12 +74,12 @@ func TestReplicatedJob(t *testing.T) {
|
||||
swarm.ServiceWithCommand([]string{"true"}),
|
||||
)
|
||||
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(
|
||||
result, err := apiClient.ServiceInspect(
|
||||
ctx, id, client.ServiceInspectOptions{},
|
||||
)
|
||||
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
|
||||
@@ -107,33 +107,33 @@ func TestUpdateReplicatedJob(t *testing.T) {
|
||||
swarm.ServiceWithCommand([]string{"true"}),
|
||||
)
|
||||
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(
|
||||
result, err := apiClient.ServiceInspect(
|
||||
ctx, id, client.ServiceInspectOptions{},
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// 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.
|
||||
spec := service.Spec
|
||||
spec := result.Service.Spec
|
||||
spec.TaskTemplate.ForceUpdate++
|
||||
|
||||
_, err = apiClient.ServiceUpdate(
|
||||
ctx, id, service.Version, spec, client.ServiceUpdateOptions{},
|
||||
ctx, id, result.Service.Version, spec, client.ServiceUpdateOptions{},
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
service2, _, err := apiClient.ServiceInspectWithRaw(
|
||||
result2, err := apiClient.ServiceInspect(
|
||||
ctx, id, client.ServiceInspectOptions{},
|
||||
)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// assert that the job iteration has increased
|
||||
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.
|
||||
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
|
||||
// bespoke closure right here.
|
||||
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),
|
||||
})
|
||||
|
||||
running := 0
|
||||
for _, task := range taskResult.Tasks {
|
||||
for _, task := range taskList.Items {
|
||||
if task.Status.State == swarmtypes.TaskStateRunning {
|
||||
running++
|
||||
}
|
||||
@@ -71,25 +71,25 @@ func TestServiceListWithStatuses(t *testing.T) {
|
||||
default:
|
||||
return poll.Continue(
|
||||
"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.
|
||||
resp, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||
result, err := apiClient.ServiceList(ctx, client.ServiceListOptions{})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Len(resp, serviceCount))
|
||||
for _, service := range resp {
|
||||
assert.Check(t, is.Len(result.Services, serviceCount))
|
||||
for _, service := range result.Services {
|
||||
assert.Check(t, is.Nil(service.ServiceStatus))
|
||||
}
|
||||
|
||||
// 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.Check(t, is.Len(resp, serviceCount))
|
||||
for _, service := range resp {
|
||||
assert.Check(t, is.Len(result.Services, serviceCount))
|
||||
for _, service := range result.Services {
|
||||
replicas := *service.Spec.Mode.Replicated.Replicas
|
||||
|
||||
assert.Assert(t, service.ServiceStatus != nil)
|
||||
|
||||
@@ -171,7 +171,7 @@ func TestSwarmScopedNetFromConfig(t *testing.T) {
|
||||
swarm.ServiceWithNetwork(swarmNetName),
|
||||
)
|
||||
defer func() {
|
||||
err := c.ServiceRemove(ctx, serviceID)
|
||||
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}()
|
||||
|
||||
@@ -246,7 +246,7 @@ func TestDockerIngressChainPosition(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
defer func() {
|
||||
err := c.ServiceRemove(ctx, serviceID)
|
||||
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}()
|
||||
|
||||
@@ -306,7 +306,7 @@ func TestRestoreIngressRulesOnFirewalldReload(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
defer func() {
|
||||
err := c.ServiceRemove(ctx, serviceID)
|
||||
_, err := c.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}()
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ func TestServiceUpdateLabel(t *testing.T) {
|
||||
service = getService(ctx, t, apiClient, serviceID)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func TestServiceUpdateSecrets(t *testing.T) {
|
||||
service = getService(ctx, t, apiClient, serviceID)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ func TestServiceUpdateConfigs(t *testing.T) {
|
||||
service = getService(ctx, t, apiClient, serviceID)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ func TestServiceUpdateNetwork(t *testing.T) {
|
||||
err = apiClient.NetworkRemove(ctx, overlayID)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = apiClient.ServiceRemove(ctx, serviceID)
|
||||
_, err = apiClient.ServiceRemove(ctx, serviceID, client.ServiceRemoveOptions{})
|
||||
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)
|
||||
}
|
||||
|
||||
func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) container.InspectResponse {
|
||||
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"),
|
||||
})
|
||||
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.Equal(t, ctr.State.Running, true)
|
||||
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 {
|
||||
t.Helper()
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
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 {
|
||||
return func(log poll.LogT) poll.Result {
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
switch {
|
||||
case err != nil:
|
||||
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return poll.Error(err)
|
||||
}
|
||||
service := result.Service
|
||||
switch {
|
||||
case service.UpdateStatus != nil && service.UpdateStatus.State == swarmtypes.UpdateStateCompleted:
|
||||
return poll.Success()
|
||||
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 {
|
||||
return func(log poll.LogT) poll.Result {
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
result, err := apiClient.ServiceInspect(ctx, serviceID, client.ServiceInspectOptions{})
|
||||
switch {
|
||||
case err != nil:
|
||||
return poll.Error(err)
|
||||
case service.Version.Index > serviceOldVersion:
|
||||
case result.Service.Version.Index > serviceOldVersion:
|
||||
return poll.Success()
|
||||
default:
|
||||
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)
|
||||
defer cli.Close()
|
||||
|
||||
service, _, err := cli.ServiceInspectWithRaw(ctx, id, client.ServiceInspectOptions{})
|
||||
res, err := cli.ServiceInspect(ctx, id, client.ServiceInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
return &service
|
||||
return &res.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,
|
||||
}
|
||||
|
||||
result, err := cli.TaskList(ctx, options)
|
||||
taskList, err := cli.TaskList(ctx, options)
|
||||
assert.NilError(t, err)
|
||||
return result.Tasks
|
||||
return taskList.Items
|
||||
}
|
||||
|
||||
// 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)
|
||||
defer cli.Close()
|
||||
|
||||
err := cli.ServiceRemove(ctx, id)
|
||||
_, err := cli.ServiceRemove(ctx, id, client.ServiceRemoveOptions{})
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
@@ -105,9 +105,9 @@ func (d *Daemon) ListServices(ctx context.Context, t testing.TB) []swarm.Service
|
||||
cli := d.NewClientT(t)
|
||||
defer cli.Close()
|
||||
|
||||
services, err := cli.ServiceList(ctx, client.ServiceListOptions{})
|
||||
res, err := cli.ServiceList(ctx, client.ServiceListOptions{})
|
||||
assert.NilError(t, err)
|
||||
return services
|
||||
return res.Services
|
||||
}
|
||||
|
||||
// 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)
|
||||
defer cli.Close()
|
||||
|
||||
result, err := cli.TaskInspect(ctx, id)
|
||||
result, err := cli.TaskInspect(ctx, id, client.TaskInspectOptions{})
|
||||
assert.NilError(t, err)
|
||||
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
|
||||
type ServiceAPIClient interface {
|
||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error)
|
||||
ServiceInspectWithRaw(ctx context.Context, serviceID string, options ServiceInspectOptions) (swarm.Service, []byte, error)
|
||||
ServiceList(ctx context.Context, options ServiceListOptions) ([]swarm.Service, error)
|
||||
ServiceRemove(ctx context.Context, serviceID string) error
|
||||
ServiceUpdate(ctx context.Context, serviceID string, version swarm.Version, service swarm.ServiceSpec, options ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error)
|
||||
ServiceLogs(ctx context.Context, serviceID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
||||
TaskLogs(ctx context.Context, taskID string, options ContainerLogsOptions) (io.ReadCloser, error)
|
||||
TaskInspect(ctx context.Context, taskID string) (TaskInspectResult, error)
|
||||
ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (ServiceCreateResult, error)
|
||||
ServiceInspect(ctx context.Context, serviceID string, options ServiceInspectOptions) (ServiceInspectResult, error)
|
||||
ServiceList(ctx context.Context, options ServiceListOptions) (ServiceListResult, 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) (ServiceUpdateResult, error)
|
||||
ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error)
|
||||
TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error)
|
||||
TaskInspect(ctx context.Context, taskID string, options TaskInspectOptions) (TaskInspectResult, 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"
|
||||
)
|
||||
|
||||
// ServiceCreate creates a new service.
|
||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options ServiceCreateOptions) (swarm.ServiceCreateResponse, error) {
|
||||
var response swarm.ServiceCreateResponse
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
if service.TaskTemplate.ContainerSpec == nil && (service.TaskTemplate.Runtime == "" || service.TaskTemplate.Runtime == swarm.RuntimeContainer) {
|
||||
service.TaskTemplate.ContainerSpec = &swarm.ContainerSpec{}
|
||||
}
|
||||
|
||||
if err := validateServiceSpec(service); err != nil {
|
||||
return response, err
|
||||
return ServiceCreateResult{}, err
|
||||
}
|
||||
|
||||
// ensure that the image is tagged
|
||||
var resolveWarning string
|
||||
var warnings []string
|
||||
switch {
|
||||
case service.TaskTemplate.ContainerSpec != nil:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
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:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return response, err
|
||||
return ServiceCreateResult{}, err
|
||||
}
|
||||
|
||||
var response swarm.ServiceCreateResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if resolveWarning != "" {
|
||||
response.Warnings = append(response.Warnings, resolveWarning)
|
||||
}
|
||||
warnings = append(warnings, response.Warnings...)
|
||||
|
||||
return response, err
|
||||
return ServiceCreateResult{
|
||||
ID: response.ID,
|
||||
Warnings: warnings,
|
||||
}, err
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/moby/moby/api/types/swarm"
|
||||
)
|
||||
|
||||
// ServiceInspectWithRaw returns the service information and the raw data.
|
||||
func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string, opts ServiceInspectOptions) (swarm.Service, []byte, error) {
|
||||
// ServiceInspectOptions holds parameters related to the service inspect operation.
|
||||
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)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
return ServiceInspectResult{}, err
|
||||
}
|
||||
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
return ServiceInspectResult{}, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, err
|
||||
}
|
||||
|
||||
var response swarm.Service
|
||||
rdr := bytes.NewReader(body)
|
||||
err = json.NewDecoder(rdr).Decode(&response)
|
||||
return response, body, err
|
||||
var out ServiceInspectResult
|
||||
out.Raw, err = decodeWithRaw(resp, &out.Service)
|
||||
return out, 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"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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{}
|
||||
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ServiceListResult{}, err
|
||||
}
|
||||
|
||||
var services []swarm.Service
|
||||
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"
|
||||
"io"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ServiceLogsResult{}, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
@@ -30,7 +50,7 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Co
|
||||
if options.Since != "" {
|
||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||
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)
|
||||
}
|
||||
@@ -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)
|
||||
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"
|
||||
|
||||
// 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.
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
return ServiceRemoveResult{}, err
|
||||
}
|
||||
|
||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||
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"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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)
|
||||
if err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
if err := validateServiceSpec(service); err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
@@ -36,21 +72,23 @@ func (cli *Client) ServiceUpdate(ctx context.Context, serviceID string, version
|
||||
query.Set("version", version.String())
|
||||
|
||||
// ensure that the image is tagged
|
||||
var resolveWarning string
|
||||
var warnings []string
|
||||
switch {
|
||||
case service.TaskTemplate.ContainerSpec != nil:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.ContainerSpec.Image); taggedImg != "" {
|
||||
service.TaskTemplate.ContainerSpec.Image = taggedImg
|
||||
}
|
||||
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:
|
||||
if taggedImg := imageWithTagString(service.TaskTemplate.PluginSpec.Remote); taggedImg != "" {
|
||||
service.TaskTemplate.PluginSpec.Remote = taggedImg
|
||||
}
|
||||
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)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.ServiceUpdateResponse{}, err
|
||||
return ServiceUpdateResult{}, err
|
||||
}
|
||||
|
||||
var response swarm.ServiceUpdateResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if resolveWarning != "" {
|
||||
response.Warnings = append(response.Warnings, resolveWarning)
|
||||
}
|
||||
|
||||
return response, err
|
||||
warnings = append(warnings, response.Warnings...)
|
||||
return ServiceUpdateResult{Warnings: warnings}, 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.
|
||||
type SwarmJoinResult struct{}
|
||||
type SwarmJoinResult struct {
|
||||
// No fields currently; placeholder for future use
|
||||
}
|
||||
|
||||
// SwarmJoin joins the swarm.
|
||||
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"
|
||||
)
|
||||
|
||||
// TaskInspectOptions contains options for inspecting a task.
|
||||
type TaskInspectOptions struct {
|
||||
// Currently no options are defined.
|
||||
}
|
||||
|
||||
// TaskInspectResult contains the result of a task inspection.
|
||||
type TaskInspectResult struct {
|
||||
Task swarm.Task
|
||||
@@ -13,7 +18,7 @@ type TaskInspectResult struct {
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
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.
|
||||
type TaskListResult struct {
|
||||
Tasks []swarm.Task
|
||||
Items []swarm.Task
|
||||
}
|
||||
|
||||
// TaskList returns the list of tasks.
|
||||
@@ -32,5 +32,5 @@ func (cli *Client) TaskList(ctx context.Context, options TaskListOptions) (TaskL
|
||||
|
||||
var tasks []swarm.Task
|
||||
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"
|
||||
"io"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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.
|
||||
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{}
|
||||
if options.ShowStdout {
|
||||
query.Set("stdout", "1")
|
||||
@@ -24,7 +44,7 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options Containe
|
||||
if options.Since != "" {
|
||||
ts, err := timestamp.GetTimestamp(options.Since, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return TaskLogsResult{}, err
|
||||
}
|
||||
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)
|
||||
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