client: refactor Events, Info, RegistryLogin
| ... | ... |
@@ -7,9 +7,7 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/moby/moby/api/types" |
| 9 | 9 |
"github.com/moby/moby/api/types/container" |
| 10 |
- "github.com/moby/moby/api/types/events" |
|
| 11 | 10 |
"github.com/moby/moby/api/types/network" |
| 12 |
- "github.com/moby/moby/api/types/registry" |
|
| 13 | 11 |
"github.com/moby/moby/api/types/system" |
| 14 | 12 |
) |
| 15 | 13 |
|
| ... | ... |
@@ -176,9 +174,9 @@ type SwarmAPIClient interface {
|
| 176 | 176 |
|
| 177 | 177 |
// SystemAPIClient defines API client methods for the system |
| 178 | 178 |
type SystemAPIClient interface {
|
| 179 |
- Events(ctx context.Context, options EventsListOptions) (<-chan events.Message, <-chan error) |
|
| 180 |
- Info(ctx context.Context) (system.Info, error) |
|
| 181 |
- RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) |
|
| 179 |
+ Events(ctx context.Context, options EventsListOptions) EventsResult |
|
| 180 |
+ Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) |
|
| 181 |
+ RegistryLogin(ctx context.Context, auth RegistryLoginOptions) (RegistryLoginResult, error) |
|
| 182 | 182 |
DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) |
| 183 | 183 |
Ping(ctx context.Context, options PingOptions) (PingResult, error) |
| 184 | 184 |
} |
| ... | ... |
@@ -408,13 +408,13 @@ func TestNegotiateAPIVersionAutomatic(t *testing.T) {
|
| 408 | 408 |
// First request should trigger negotiation |
| 409 | 409 |
pingVersion = "1.50" |
| 410 | 410 |
expected = "1.50" |
| 411 |
- _, _ = client.Info(ctx) |
|
| 411 |
+ _, _ = client.Info(ctx, InfoOptions{})
|
|
| 412 | 412 |
assert.Check(t, is.Equal(client.ClientVersion(), expected)) |
| 413 | 413 |
|
| 414 | 414 |
// Once successfully negotiated, subsequent requests should not re-negotiate |
| 415 | 415 |
pingVersion = "1.49" |
| 416 | 416 |
expected = "1.50" |
| 417 |
- _, _ = client.Info(ctx) |
|
| 417 |
+ _, _ = client.Info(ctx, InfoOptions{})
|
|
| 418 | 418 |
assert.Check(t, is.Equal(client.ClientVersion(), expected)) |
| 419 | 419 |
} |
| 420 | 420 |
|
| ... | ... |
@@ -8,17 +8,38 @@ import ( |
| 8 | 8 |
"github.com/moby/moby/api/types/registry" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+type RegistryLoginOptions struct {
|
|
| 12 |
+ Username string |
|
| 13 |
+ Password string |
|
| 14 |
+ ServerAddress string |
|
| 15 |
+ IdentityToken string |
|
| 16 |
+ RegistryToken string |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// RegistryLoginResult holds the result of a RegistryLogin query. |
|
| 20 |
+type RegistryLoginResult struct {
|
|
| 21 |
+ Auth registry.AuthenticateOKBody |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 11 | 24 |
// RegistryLogin authenticates the docker server with a given docker registry. |
| 12 | 25 |
// It returns unauthorizedError when the authentication fails. |
| 13 |
-func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
|
|
| 26 |
+func (cli *Client) RegistryLogin(ctx context.Context, options RegistryLoginOptions) (RegistryLoginResult, error) {
|
|
| 27 |
+ auth := registry.AuthConfig{
|
|
| 28 |
+ Username: options.Username, |
|
| 29 |
+ Password: options.Password, |
|
| 30 |
+ ServerAddress: options.ServerAddress, |
|
| 31 |
+ IdentityToken: options.IdentityToken, |
|
| 32 |
+ RegistryToken: options.RegistryToken, |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 14 | 35 |
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
|
| 15 | 36 |
defer ensureReaderClosed(resp) |
| 16 | 37 |
|
| 17 | 38 |
if err != nil {
|
| 18 |
- return registry.AuthenticateOKBody{}, err
|
|
| 39 |
+ return RegistryLoginResult{}, err
|
|
| 19 | 40 |
} |
| 20 | 41 |
|
| 21 | 42 |
var response registry.AuthenticateOKBody |
| 22 | 43 |
err = json.NewDecoder(resp.Body).Decode(&response) |
| 23 |
- return response, err |
|
| 44 |
+ return RegistryLoginResult{Auth: response}, err
|
|
| 24 | 45 |
} |
| ... | ... |
@@ -19,11 +19,17 @@ type EventsListOptions struct {
|
| 19 | 19 |
Filters Filters |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
+// EventsResult holds the result of an Events query. |
|
| 23 |
+type EventsResult struct {
|
|
| 24 |
+ Messages <-chan events.Message |
|
| 25 |
+ Err <-chan error |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 22 | 28 |
// Events returns a stream of events in the daemon. It's up to the caller to close the stream |
| 23 | 29 |
// by cancelling the context. Once the stream has been completely read an [io.EOF] error is |
| 24 | 30 |
// sent over the error channel. If an error is sent, all processing is stopped. It's up |
| 25 | 31 |
// to the caller to reopen the stream in the event of an error by reinvoking this method. |
| 26 |
-func (cli *Client) Events(ctx context.Context, options EventsListOptions) (<-chan events.Message, <-chan error) {
|
|
| 32 |
+func (cli *Client) Events(ctx context.Context, options EventsListOptions) EventsResult {
|
|
| 27 | 33 |
messages := make(chan events.Message) |
| 28 | 34 |
errs := make(chan error, 1) |
| 29 | 35 |
|
| ... | ... |
@@ -76,7 +82,10 @@ func (cli *Client) Events(ctx context.Context, options EventsListOptions) (<-cha |
| 76 | 76 |
}() |
| 77 | 77 |
<-started |
| 78 | 78 |
|
| 79 |
- return messages, errs |
|
| 79 |
+ return EventsResult{
|
|
| 80 |
+ Messages: messages, |
|
| 81 |
+ Err: errs, |
|
| 82 |
+ } |
|
| 80 | 83 |
} |
| 81 | 84 |
|
| 82 | 85 |
func buildEventsQueryParams(options EventsListOptions) (url.Values, error) {
|
| ... | ... |
@@ -37,8 +37,8 @@ func TestEventsErrorInOptions(t *testing.T) {
|
| 37 | 37 |
for _, tc := range errorCases {
|
| 38 | 38 |
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) |
| 39 | 39 |
assert.NilError(t, err) |
| 40 |
- _, errs := client.Events(context.Background(), tc.options) |
|
| 41 |
- err = <-errs |
|
| 40 |
+ events := client.Events(context.Background(), tc.options) |
|
| 41 |
+ err = <-events.Err |
|
| 42 | 42 |
assert.Check(t, is.ErrorContains(err, tc.expectedError)) |
| 43 | 43 |
} |
| 44 | 44 |
} |
| ... | ... |
@@ -46,8 +46,8 @@ func TestEventsErrorInOptions(t *testing.T) {
|
| 46 | 46 |
func TestEventsErrorFromServer(t *testing.T) {
|
| 47 | 47 |
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) |
| 48 | 48 |
assert.NilError(t, err) |
| 49 |
- _, errs := client.Events(context.Background(), EventsListOptions{})
|
|
| 50 |
- err = <-errs |
|
| 49 |
+ events := client.Events(context.Background(), EventsListOptions{})
|
|
| 50 |
+ err = <-events.Err |
|
| 51 | 51 |
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) |
| 52 | 52 |
} |
| 53 | 53 |
|
| ... | ... |
@@ -133,18 +133,18 @@ func TestEvents(t *testing.T) {
|
| 133 | 133 |
})) |
| 134 | 134 |
assert.NilError(t, err) |
| 135 | 135 |
|
| 136 |
- messages, errs := client.Events(context.Background(), eventsCase.options) |
|
| 136 |
+ events := client.Events(context.Background(), eventsCase.options) |
|
| 137 | 137 |
|
| 138 | 138 |
loop: |
| 139 | 139 |
for {
|
| 140 | 140 |
select {
|
| 141 |
- case err := <-errs: |
|
| 141 |
+ case err := <-events.Err: |
|
| 142 | 142 |
if err != nil && !errors.Is(err, io.EOF) {
|
| 143 | 143 |
t.Fatal(err) |
| 144 | 144 |
} |
| 145 | 145 |
|
| 146 | 146 |
break loop |
| 147 |
- case e := <-messages: |
|
| 147 |
+ case e := <-events.Messages: |
|
| 148 | 148 |
_, ok := eventsCase.expectedEvents[e.Actor.ID] |
| 149 | 149 |
assert.Check(t, ok, "event received not expected with action %s & id %s", e.Action, e.Actor.ID) |
| 150 | 150 |
} |
| ... | ... |
@@ -9,18 +9,26 @@ import ( |
| 9 | 9 |
"github.com/moby/moby/api/types/system" |
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 |
+type InfoOptions struct {
|
|
| 13 |
+ // No options currently; placeholder for future use |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+type SystemInfoResult struct {
|
|
| 17 |
+ Info system.Info |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 12 | 20 |
// Info returns information about the docker server. |
| 13 |
-func (cli *Client) Info(ctx context.Context) (system.Info, error) {
|
|
| 14 |
- var info system.Info |
|
| 21 |
+func (cli *Client) Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) {
|
|
| 15 | 22 |
resp, err := cli.get(ctx, "/info", url.Values{}, nil)
|
| 16 | 23 |
defer ensureReaderClosed(resp) |
| 17 | 24 |
if err != nil {
|
| 18 |
- return info, err |
|
| 25 |
+ return SystemInfoResult{}, err
|
|
| 19 | 26 |
} |
| 20 | 27 |
|
| 28 |
+ var info system.Info |
|
| 21 | 29 |
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
| 22 |
- return info, fmt.Errorf("Error reading remote info: %v", err)
|
|
| 30 |
+ return SystemInfoResult{}, fmt.Errorf("Error reading remote info: %v", err)
|
|
| 23 | 31 |
} |
| 24 | 32 |
|
| 25 |
- return info, nil |
|
| 33 |
+ return SystemInfoResult{Info: info}, nil
|
|
| 26 | 34 |
} |
| ... | ... |
@@ -14,14 +14,14 @@ import ( |
| 14 | 14 |
func TestInfoServerError(t *testing.T) {
|
| 15 | 15 |
client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error"))) |
| 16 | 16 |
assert.NilError(t, err) |
| 17 |
- _, err = client.Info(context.Background()) |
|
| 17 |
+ _, err = client.Info(context.Background(), InfoOptions{})
|
|
| 18 | 18 |
assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal)) |
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 | 21 |
func TestInfoInvalidResponseJSONError(t *testing.T) {
|
| 22 | 22 |
client, err := NewClientWithOpts(WithMockClient(mockResponse(http.StatusOK, nil, "invalid json"))) |
| 23 | 23 |
assert.NilError(t, err) |
| 24 |
- _, err = client.Info(context.Background()) |
|
| 24 |
+ _, err = client.Info(context.Background(), InfoOptions{})
|
|
| 25 | 25 |
assert.Check(t, is.ErrorContains(err, "invalid character")) |
| 26 | 26 |
} |
| 27 | 27 |
|
| ... | ... |
@@ -38,8 +38,9 @@ func TestInfo(t *testing.T) {
|
| 38 | 38 |
})) |
| 39 | 39 |
assert.NilError(t, err) |
| 40 | 40 |
|
| 41 |
- info, err := client.Info(context.Background()) |
|
| 41 |
+ result, err := client.Info(context.Background(), InfoOptions{})
|
|
| 42 | 42 |
assert.NilError(t, err) |
| 43 |
+ info := result.Info |
|
| 43 | 44 |
|
| 44 | 45 |
assert.Check(t, is.Equal(info.ID, "daemonID")) |
| 45 | 46 |
assert.Check(t, is.Equal(info.Containers, 3)) |
| ... | ... |
@@ -68,8 +69,9 @@ func TestInfoWithDiscoveredDevices(t *testing.T) {
|
| 68 | 68 |
})) |
| 69 | 69 |
assert.NilError(t, err) |
| 70 | 70 |
|
| 71 |
- info, err := client.Info(context.Background()) |
|
| 71 |
+ result, err := client.Info(context.Background(), InfoOptions{})
|
|
| 72 | 72 |
assert.NilError(t, err) |
| 73 |
+ info := result.Info |
|
| 73 | 74 |
|
| 74 | 75 |
assert.Check(t, is.Equal(info.ID, "daemonID")) |
| 75 | 76 |
assert.Check(t, is.Equal(info.Containers, 3)) |
| ... | ... |
@@ -756,9 +756,9 @@ func checkClusterHealth(t *testing.T, cl []*daemon.Daemon, managerCount, workerC |
| 756 | 756 |
|
| 757 | 757 |
// check info in a poll.WaitOn(), because if the cluster doesn't have a leader, `info` will return an error |
| 758 | 758 |
checkInfo := func(t *testing.T) (any, string) {
|
| 759 |
- client := d.NewClientT(t) |
|
| 760 |
- daemonInfo, err := client.Info(ctx) |
|
| 761 |
- info = daemonInfo.Swarm |
|
| 759 |
+ apiClient := d.NewClientT(t) |
|
| 760 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 761 |
+ info = result.Info.Swarm |
|
| 762 | 762 |
return err, "cluster not ready in time" |
| 763 | 763 |
} |
| 764 | 764 |
poll.WaitOn(t, pollCheck(t, checkInfo, checker.IsNil()), poll.WithTimeout(defaultReconciliationTimeout)) |
| ... | ... |
@@ -21,8 +21,9 @@ func (s *DockerCLIInfoSuite) TestInfoSecurityOptions(c *testing.T) {
|
| 21 | 21 |
apiClient, err := client.NewClientWithOpts(client.FromEnv) |
| 22 | 22 |
assert.NilError(c, err) |
| 23 | 23 |
defer apiClient.Close() |
| 24 |
- info, err := apiClient.Info(testutil.GetContext(c)) |
|
| 24 |
+ result, err := apiClient.Info(testutil.GetContext(c), client.InfoOptions{})
|
|
| 25 | 25 |
assert.NilError(c, err) |
| 26 |
+ info := result.Info |
|
| 26 | 27 |
|
| 27 | 28 |
if Apparmor() {
|
| 28 | 29 |
assert.Check(c, is.Contains(info.SecurityOptions, "name=apparmor")) |
| ... | ... |
@@ -54,8 +54,9 @@ func (s *DockerCLIPluginLogDriverSuite) TestPluginLogDriverInfoList(c *testing.T |
| 54 | 54 |
assert.NilError(c, err) |
| 55 | 55 |
defer apiClient.Close() |
| 56 | 56 |
|
| 57 |
- info, err := apiClient.Info(testutil.GetContext(c)) |
|
| 57 |
+ result, err := apiClient.Info(testutil.GetContext(c), client.InfoOptions{})
|
|
| 58 | 58 |
assert.NilError(c, err) |
| 59 |
+ info := result.Info |
|
| 59 | 60 |
|
| 60 | 61 |
drivers := strings.Join(info.Plugins.Log, " ") |
| 61 | 62 |
assert.Assert(c, is.Contains(drivers, "json-file")) |
| ... | ... |
@@ -200,8 +200,9 @@ func daemonTime(t *testing.T) time.Time {
|
| 200 | 200 |
assert.NilError(t, err) |
| 201 | 201 |
defer apiClient.Close() |
| 202 | 202 |
|
| 203 |
- info, err := apiClient.Info(testutil.GetContext(t)) |
|
| 203 |
+ result, err := apiClient.Info(testutil.GetContext(t), client.InfoOptions{})
|
|
| 204 | 204 |
assert.NilError(t, err) |
| 205 |
+ info := result.Info |
|
| 205 | 206 |
|
| 206 | 207 |
dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) |
| 207 | 208 |
assert.Assert(t, err == nil, "invalid time format in GET /info response") |
| ... | ... |
@@ -286,11 +287,11 @@ func minimalBaseImage() string {
|
| 286 | 286 |
} |
| 287 | 287 |
|
| 288 | 288 |
func getGoroutineNumber(ctx context.Context, apiClient client.APIClient) (int, error) {
|
| 289 |
- info, err := apiClient.Info(ctx) |
|
| 289 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 290 | 290 |
if err != nil {
|
| 291 | 291 |
return 0, err |
| 292 | 292 |
} |
| 293 |
- return info.NGoroutines, nil |
|
| 293 |
+ return result.Info.NGoroutines, nil |
|
| 294 | 294 |
} |
| 295 | 295 |
|
| 296 | 296 |
func waitForStableGoroutineCount(ctx context.Context, t poll.TestingT, apiClient client.APIClient) int {
|
| ... | ... |
@@ -740,10 +740,12 @@ func TestBuildEmitsImageCreateEvent(t *testing.T) {
|
| 740 | 740 |
assert.NilError(t, err) |
| 741 | 741 |
buildLogs := out.String() |
| 742 | 742 |
|
| 743 |
- eventsChan, errs := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 743 |
+ result := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 744 | 744 |
Since: since.Format(time.RFC3339Nano), |
| 745 | 745 |
Until: time.Now().Format(time.RFC3339Nano), |
| 746 | 746 |
}) |
| 747 |
+ eventsChan := result.Messages |
|
| 748 |
+ errs := result.Err |
|
| 747 | 749 |
|
| 748 | 750 |
var eventsReceived []string |
| 749 | 751 |
imageCreateEvts := 0 |
| ... | ... |
@@ -188,8 +188,9 @@ func TestCDIInfoDiscoveredDevices(t *testing.T) {
|
| 188 | 188 |
defer d.Stop(t) |
| 189 | 189 |
|
| 190 | 190 |
c := d.NewClientT(t) |
| 191 |
- info, err := c.Info(ctx) |
|
| 191 |
+ result, err := c.Info(ctx, client.InfoOptions{})
|
|
| 192 | 192 |
assert.NilError(t, err) |
| 193 |
+ info := result.Info |
|
| 193 | 194 |
|
| 194 | 195 |
assert.Check(t, is.Len(info.CDISpecDirs, 1)) |
| 195 | 196 |
assert.Check(t, is.Equal(info.CDISpecDirs[0], cdiDir)) |
| ... | ... |
@@ -807,9 +807,10 @@ func TestContainerdContainerImageInfo(t *testing.T) {
|
| 807 | 807 |
apiClient := testEnv.APIClient() |
| 808 | 808 |
defer apiClient.Close() |
| 809 | 809 |
|
| 810 |
- info, err := apiClient.Info(ctx) |
|
| 810 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 811 | 811 |
assert.NilError(t, err) |
| 812 | 812 |
|
| 813 |
+ info := result.Info |
|
| 813 | 814 |
skip.If(t, info.Containerd == nil, "requires containerd") |
| 814 | 815 |
|
| 815 | 816 |
// Currently a containerd container is only created when the container is started. |
| ... | ... |
@@ -98,10 +98,11 @@ func TestMountDaemonRoot(t *testing.T) {
|
| 98 | 98 |
|
| 99 | 99 |
ctx := setupTest(t) |
| 100 | 100 |
apiClient := testEnv.APIClient() |
| 101 |
- info, err := apiClient.Info(ctx) |
|
| 101 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 102 | 102 |
if err != nil {
|
| 103 | 103 |
t.Fatal(err) |
| 104 | 104 |
} |
| 105 |
+ info := result.Info |
|
| 105 | 106 |
|
| 106 | 107 |
for _, test := range []struct {
|
| 107 | 108 |
desc string |
| ... | ... |
@@ -39,11 +39,13 @@ func TestPause(t *testing.T) {
|
| 39 | 39 |
|
| 40 | 40 |
until := request.DaemonUnixTime(ctx, t, apiClient, testEnv) |
| 41 | 41 |
|
| 42 |
- messages, errs := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 42 |
+ result := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 43 | 43 |
Since: since, |
| 44 | 44 |
Until: until, |
| 45 | 45 |
Filters: make(client.Filters).Add(string(events.ContainerEventType), cID), |
| 46 | 46 |
}) |
| 47 |
+ messages := result.Messages |
|
| 48 |
+ errs := result.Err |
|
| 47 | 49 |
assert.Check(t, is.DeepEqual([]events.Action{events.ActionPause, events.ActionUnPause}, getEventActions(t, messages, errs)))
|
| 48 | 50 |
} |
| 49 | 51 |
|
| ... | ... |
@@ -247,9 +247,11 @@ func TestContainerRestartWithCancelledRequest(t *testing.T) {
|
| 247 | 247 |
}() |
| 248 | 248 |
|
| 249 | 249 |
// Start listening for events. |
| 250 |
- messages, errs := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 250 |
+ result := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 251 | 251 |
Filters: make(client.Filters).Add("container", cID).Add("event", string(events.ActionRestart)),
|
| 252 | 252 |
}) |
| 253 |
+ messages := result.Messages |
|
| 254 |
+ errs := result.Err |
|
| 253 | 255 |
|
| 254 | 256 |
// Make restart request, but cancel the request before the container |
| 255 | 257 |
// is (forcibly) killed. |
| ... | ... |
@@ -21,9 +21,10 @@ func TestStats(t *testing.T) {
|
| 21 | 21 |
ctx := setupTest(t) |
| 22 | 22 |
apiClient := testEnv.APIClient() |
| 23 | 23 |
|
| 24 |
- info, err := apiClient.Info(ctx) |
|
| 24 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 25 | 25 |
assert.NilError(t, err) |
| 26 | 26 |
|
| 27 |
+ info := result.Info |
|
| 27 | 28 |
cID := container.Run(ctx, t, apiClient) |
| 28 | 29 |
t.Run("no-stream", func(t *testing.T) {
|
| 29 | 30 |
resp, err := apiClient.ContainerStats(ctx, cID, client.ContainerStatsOptions{
|
| ... | ... |
@@ -70,9 +70,9 @@ func CheckGoroutineCount(ctx context.Context, apiClient client.SystemAPIClient, |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
func getGoroutineNumber(ctx context.Context, apiClient client.SystemAPIClient) (int, error) {
|
| 73 |
- info, err := apiClient.Info(ctx) |
|
| 73 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 74 | 74 |
if err != nil {
|
| 75 | 75 |
return 0, err |
| 76 | 76 |
} |
| 77 |
- return info.NGoroutines, nil |
|
| 77 |
+ return result.Info.NGoroutines, nil |
|
| 78 | 78 |
} |
| ... | ... |
@@ -31,8 +31,9 @@ func TestInfoFirewallBackend(t *testing.T) {
|
| 31 | 31 |
if !testEnv.IsRootless() && networking.FirewalldRunning() {
|
| 32 | 32 |
expDriver += "+firewalld" |
| 33 | 33 |
} |
| 34 |
- info, err := c.Info(ctx) |
|
| 34 |
+ result, err := c.Info(ctx, client.InfoOptions{})
|
|
| 35 | 35 |
assert.NilError(t, err) |
| 36 |
+ info := result.Info |
|
| 36 | 37 |
assert.Assert(t, info.FirewallBackend != nil, "expected firewall backend in info response") |
| 37 | 38 |
t.Log("FirewallBackend: Driver:", info.FirewallBackend.Driver)
|
| 38 | 39 |
for _, kv := range info.FirewallBackend.Info {
|
| ... | ... |
@@ -43,8 +44,9 @@ func TestInfoFirewallBackend(t *testing.T) {
|
| 43 | 43 |
// Check FirewallBackend is omitted for API <= 1.48. |
| 44 | 44 |
t.Run("api 1.48", func(t *testing.T) {
|
| 45 | 45 |
c148 := request.NewAPIClient(t, client.WithVersion("1.48"))
|
| 46 |
- info148, err := c148.Info(ctx) |
|
| 46 |
+ result, err := c148.Info(ctx, client.InfoOptions{})
|
|
| 47 | 47 |
assert.NilError(t, err) |
| 48 |
+ info148 := result.Info |
|
| 48 | 49 |
assert.Check(t, is.Nil(info148.FirewallBackend)) |
| 49 | 50 |
}) |
| 50 | 51 |
} |
| ... | ... |
@@ -276,9 +276,10 @@ func systemTime(ctx context.Context, t *testing.T, apiClient client.APIClient, t |
| 276 | 276 |
return time.Now() |
| 277 | 277 |
} |
| 278 | 278 |
|
| 279 |
- info, err := apiClient.Info(ctx) |
|
| 279 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 280 | 280 |
assert.NilError(t, err) |
| 281 | 281 |
|
| 282 |
+ info := result.Info |
|
| 282 | 283 |
dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) |
| 283 | 284 |
assert.NilError(t, err, "invalid time format in GET /info response") |
| 284 | 285 |
return dt |
| ... | ... |
@@ -289,9 +290,9 @@ func systemEventsSince(ctx context.Context, apiClient client.APIClient, since st |
| 289 | 289 |
Since: since, |
| 290 | 290 |
} |
| 291 | 291 |
ctx, cancel := context.WithCancel(ctx) |
| 292 |
- events, errs := apiClient.Events(ctx, eventOptions) |
|
| 292 |
+ result := apiClient.Events(ctx, eventOptions) |
|
| 293 | 293 |
|
| 294 |
- return events, errs, cancel |
|
| 294 |
+ return result.Messages, result.Err, cancel |
|
| 295 | 295 |
} |
| 296 | 296 |
|
| 297 | 297 |
func TestAuthZPluginErrorResponse(t *testing.T) {
|
| ... | ... |
@@ -30,9 +30,11 @@ func TestEventsExecDie(t *testing.T) {
|
| 30 | 30 |
}) |
| 31 | 31 |
assert.NilError(t, err) |
| 32 | 32 |
|
| 33 |
- msg, errs := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 33 |
+ result := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 34 | 34 |
Filters: make(client.Filters).Add("container", cID).Add("event", string(events.ActionExecDie)),
|
| 35 | 35 |
}) |
| 36 |
+ msg := result.Messages |
|
| 37 |
+ errs := result.Err |
|
| 36 | 38 |
|
| 37 | 39 |
_, err = apiClient.ExecStart(ctx, res.ID, client.ExecStartOptions{
|
| 38 | 40 |
Detach: true, |
| ... | ... |
@@ -107,11 +109,13 @@ func TestEventsVolumeCreate(t *testing.T) {
|
| 107 | 107 |
Add("type", "volume").
|
| 108 | 108 |
Add("event", "create").
|
| 109 | 109 |
Add("volume", volName)
|
| 110 |
- messages, errs := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 110 |
+ result := apiClient.Events(ctx, client.EventsListOptions{
|
|
| 111 | 111 |
Since: since, |
| 112 | 112 |
Until: request.DaemonUnixTime(ctx, t, apiClient, testEnv), |
| 113 | 113 |
Filters: filter, |
| 114 | 114 |
}) |
| 115 |
+ messages := result.Messages |
|
| 116 |
+ errs := result.Err |
|
| 115 | 117 |
|
| 116 | 118 |
volEvents, err := getEvents(messages, errs) |
| 117 | 119 |
assert.NilError(t, err) |
| ... | ... |
@@ -123,11 +127,13 @@ func TestEventsVolumeCreate(t *testing.T) {
|
| 123 | 123 |
Target: "/tmp/foo", |
| 124 | 124 |
})) |
| 125 | 125 |
|
| 126 |
- messages, errs = apiClient.Events(ctx, client.EventsListOptions{
|
|
| 126 |
+ result = apiClient.Events(ctx, client.EventsListOptions{
|
|
| 127 | 127 |
Since: since, |
| 128 | 128 |
Until: request.DaemonUnixTime(ctx, t, apiClient, testEnv), |
| 129 | 129 |
Filters: filter, |
| 130 | 130 |
}) |
| 131 |
+ messages = result.Messages |
|
| 132 |
+ errs = result.Err |
|
| 131 | 133 |
|
| 132 | 134 |
volEvents, err = getEvents(messages, errs) |
| 133 | 135 |
assert.NilError(t, err) |
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 | 8 |
"github.com/moby/moby/api/types/registry" |
| 9 |
+ "github.com/moby/moby/client" |
|
| 9 | 10 |
"github.com/moby/moby/v2/internal/testutil" |
| 10 | 11 |
"github.com/moby/moby/v2/internal/testutil/daemon" |
| 11 | 12 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -17,9 +18,10 @@ func TestInfoAPI(t *testing.T) {
|
| 17 | 17 |
ctx := setupTest(t) |
| 18 | 18 |
apiClient := testEnv.APIClient() |
| 19 | 19 |
|
| 20 |
- info, err := apiClient.Info(ctx) |
|
| 20 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 21 | 21 |
assert.NilError(t, err) |
| 22 | 22 |
|
| 23 |
+ info := result.Info |
|
| 23 | 24 |
// TODO(thaJeztah): make sure we have other tests that run a local daemon and check other fields based on known state. |
| 24 | 25 |
assert.Check(t, info.ID != "") |
| 25 | 26 |
assert.Check(t, is.Equal(info.Containers, info.ContainersRunning+info.ContainersPaused+info.ContainersStopped)) |
| ... | ... |
@@ -51,9 +53,11 @@ func TestInfoAPIWarnings(t *testing.T) {
|
| 51 | 51 |
d.Start(t, "-H=0.0.0.0:23756", "-H="+d.Sock()) |
| 52 | 52 |
defer d.Stop(t) |
| 53 | 53 |
|
| 54 |
- info, err := c.Info(ctx) |
|
| 54 |
+ result, err := c.Info(ctx, client.InfoOptions{})
|
|
| 55 | 55 |
assert.NilError(t, err) |
| 56 | 56 |
|
| 57 |
+ info := result.Info |
|
| 58 |
+ |
|
| 57 | 59 |
stringsToCheck := []string{
|
| 58 | 60 |
"Access to the remote API is equivalent to root access", |
| 59 | 61 |
"http://0.0.0.0:23756", |
| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"testing" |
| 6 | 6 |
|
| 7 |
- "github.com/moby/moby/api/types/registry" |
|
| 7 |
+ "github.com/moby/moby/client" |
|
| 8 | 8 |
registrypkg "github.com/moby/moby/v2/daemon/pkg/registry" |
| 9 | 9 |
"github.com/moby/moby/v2/integration/internal/requirement" |
| 10 | 10 |
"gotest.tools/v3/assert" |
| ... | ... |
@@ -19,7 +19,7 @@ func TestLoginFailsWithBadCredentials(t *testing.T) {
|
| 19 | 19 |
ctx := setupTest(t) |
| 20 | 20 |
apiClient := testEnv.APIClient() |
| 21 | 21 |
|
| 22 |
- _, err := apiClient.RegistryLogin(ctx, registry.AuthConfig{
|
|
| 22 |
+ _, err := apiClient.RegistryLogin(ctx, client.RegistryLoginOptions{
|
|
| 23 | 23 |
Username: "no-user", |
| 24 | 24 |
Password: "no-password", |
| 25 | 25 |
}) |
| ... | ... |
@@ -985,8 +985,9 @@ func (d *Daemon) queryRootDir() (string, error) {
|
| 985 | 985 |
func (d *Daemon) Info(t testing.TB) system.Info {
|
| 986 | 986 |
t.Helper() |
| 987 | 987 |
c := d.NewClientT(t) |
| 988 |
- info, err := c.Info(context.Background()) |
|
| 988 |
+ result, err := c.Info(context.Background(), client.InfoOptions{})
|
|
| 989 | 989 |
assert.NilError(t, err) |
| 990 |
+ info := result.Info |
|
| 990 | 991 |
assert.NilError(t, c.Close()) |
| 991 | 992 |
return info |
| 992 | 993 |
} |
| ... | ... |
@@ -162,8 +162,9 @@ func (d *Daemon) SwarmLeave(ctx context.Context, t testing.TB, force bool) error |
| 162 | 162 |
func (d *Daemon) SwarmInfo(ctx context.Context, t testing.TB) swarm.Info {
|
| 163 | 163 |
t.Helper() |
| 164 | 164 |
cli := d.NewClientT(t) |
| 165 |
- info, err := cli.Info(ctx) |
|
| 165 |
+ result, err := cli.Info(ctx, client.InfoOptions{})
|
|
| 166 | 166 |
assert.NilError(t, err, "get swarm info") |
| 167 |
+ info := result.Info |
|
| 167 | 168 |
return info.Swarm |
| 168 | 169 |
} |
| 169 | 170 |
|
| ... | ... |
@@ -46,10 +46,11 @@ func New(ctx context.Context) (*Execution, error) {
|
| 46 | 46 |
|
| 47 | 47 |
// FromClient creates a new Execution environment from the passed in client |
| 48 | 48 |
func FromClient(ctx context.Context, c *client.Client) (*Execution, error) {
|
| 49 |
- info, err := c.Info(ctx) |
|
| 49 |
+ result, err := c.Info(ctx, client.InfoOptions{})
|
|
| 50 | 50 |
if err != nil {
|
| 51 | 51 |
return nil, errors.Wrapf(err, "failed to get info from daemon") |
| 52 | 52 |
} |
| 53 |
+ info := result.Info |
|
| 53 | 54 |
v, err := c.ServerVersion(context.Background()) |
| 54 | 55 |
if err != nil {
|
| 55 | 56 |
return nil, errors.Wrapf(err, "failed to get version info from daemon") |
| ... | ... |
@@ -33,14 +33,15 @@ func NewAPIClient(t testing.TB, ops ...client.Opt) client.APIClient {
|
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 | 35 |
// DaemonTime provides the current time on the daemon host |
| 36 |
-func DaemonTime(ctx context.Context, t testing.TB, client client.APIClient, testEnv *environment.Execution) time.Time {
|
|
| 36 |
+func DaemonTime(ctx context.Context, t testing.TB, apiClient client.APIClient, testEnv *environment.Execution) time.Time {
|
|
| 37 | 37 |
t.Helper() |
| 38 | 38 |
if testEnv.IsLocalDaemon() {
|
| 39 | 39 |
return time.Now() |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
- info, err := client.Info(ctx) |
|
| 42 |
+ result, err := apiClient.Info(ctx, client.InfoOptions{})
|
|
| 43 | 43 |
assert.NilError(t, err) |
| 44 |
+ info := result.Info |
|
| 44 | 45 |
|
| 45 | 46 |
dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) |
| 46 | 47 |
assert.NilError(t, err, "invalid time format in GET /info response") |
| ... | ... |
@@ -7,9 +7,7 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/moby/moby/api/types" |
| 9 | 9 |
"github.com/moby/moby/api/types/container" |
| 10 |
- "github.com/moby/moby/api/types/events" |
|
| 11 | 10 |
"github.com/moby/moby/api/types/network" |
| 12 |
- "github.com/moby/moby/api/types/registry" |
|
| 13 | 11 |
"github.com/moby/moby/api/types/system" |
| 14 | 12 |
) |
| 15 | 13 |
|
| ... | ... |
@@ -176,9 +174,9 @@ type SwarmAPIClient interface {
|
| 176 | 176 |
|
| 177 | 177 |
// SystemAPIClient defines API client methods for the system |
| 178 | 178 |
type SystemAPIClient interface {
|
| 179 |
- Events(ctx context.Context, options EventsListOptions) (<-chan events.Message, <-chan error) |
|
| 180 |
- Info(ctx context.Context) (system.Info, error) |
|
| 181 |
- RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) |
|
| 179 |
+ Events(ctx context.Context, options EventsListOptions) EventsResult |
|
| 180 |
+ Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) |
|
| 181 |
+ RegistryLogin(ctx context.Context, auth RegistryLoginOptions) (RegistryLoginResult, error) |
|
| 182 | 182 |
DiskUsage(ctx context.Context, options DiskUsageOptions) (system.DiskUsage, error) |
| 183 | 183 |
Ping(ctx context.Context, options PingOptions) (PingResult, error) |
| 184 | 184 |
} |
| ... | ... |
@@ -8,17 +8,38 @@ import ( |
| 8 | 8 |
"github.com/moby/moby/api/types/registry" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+type RegistryLoginOptions struct {
|
|
| 12 |
+ Username string |
|
| 13 |
+ Password string |
|
| 14 |
+ ServerAddress string |
|
| 15 |
+ IdentityToken string |
|
| 16 |
+ RegistryToken string |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// RegistryLoginResult holds the result of a RegistryLogin query. |
|
| 20 |
+type RegistryLoginResult struct {
|
|
| 21 |
+ Auth registry.AuthenticateOKBody |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 11 | 24 |
// RegistryLogin authenticates the docker server with a given docker registry. |
| 12 | 25 |
// It returns unauthorizedError when the authentication fails. |
| 13 |
-func (cli *Client) RegistryLogin(ctx context.Context, auth registry.AuthConfig) (registry.AuthenticateOKBody, error) {
|
|
| 26 |
+func (cli *Client) RegistryLogin(ctx context.Context, options RegistryLoginOptions) (RegistryLoginResult, error) {
|
|
| 27 |
+ auth := registry.AuthConfig{
|
|
| 28 |
+ Username: options.Username, |
|
| 29 |
+ Password: options.Password, |
|
| 30 |
+ ServerAddress: options.ServerAddress, |
|
| 31 |
+ IdentityToken: options.IdentityToken, |
|
| 32 |
+ RegistryToken: options.RegistryToken, |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 14 | 35 |
resp, err := cli.post(ctx, "/auth", url.Values{}, auth, nil)
|
| 15 | 36 |
defer ensureReaderClosed(resp) |
| 16 | 37 |
|
| 17 | 38 |
if err != nil {
|
| 18 |
- return registry.AuthenticateOKBody{}, err
|
|
| 39 |
+ return RegistryLoginResult{}, err
|
|
| 19 | 40 |
} |
| 20 | 41 |
|
| 21 | 42 |
var response registry.AuthenticateOKBody |
| 22 | 43 |
err = json.NewDecoder(resp.Body).Decode(&response) |
| 23 |
- return response, err |
|
| 44 |
+ return RegistryLoginResult{Auth: response}, err
|
|
| 24 | 45 |
} |
| ... | ... |
@@ -19,11 +19,17 @@ type EventsListOptions struct {
|
| 19 | 19 |
Filters Filters |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
+// EventsResult holds the result of an Events query. |
|
| 23 |
+type EventsResult struct {
|
|
| 24 |
+ Messages <-chan events.Message |
|
| 25 |
+ Err <-chan error |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 22 | 28 |
// Events returns a stream of events in the daemon. It's up to the caller to close the stream |
| 23 | 29 |
// by cancelling the context. Once the stream has been completely read an [io.EOF] error is |
| 24 | 30 |
// sent over the error channel. If an error is sent, all processing is stopped. It's up |
| 25 | 31 |
// to the caller to reopen the stream in the event of an error by reinvoking this method. |
| 26 |
-func (cli *Client) Events(ctx context.Context, options EventsListOptions) (<-chan events.Message, <-chan error) {
|
|
| 32 |
+func (cli *Client) Events(ctx context.Context, options EventsListOptions) EventsResult {
|
|
| 27 | 33 |
messages := make(chan events.Message) |
| 28 | 34 |
errs := make(chan error, 1) |
| 29 | 35 |
|
| ... | ... |
@@ -76,7 +82,10 @@ func (cli *Client) Events(ctx context.Context, options EventsListOptions) (<-cha |
| 76 | 76 |
}() |
| 77 | 77 |
<-started |
| 78 | 78 |
|
| 79 |
- return messages, errs |
|
| 79 |
+ return EventsResult{
|
|
| 80 |
+ Messages: messages, |
|
| 81 |
+ Err: errs, |
|
| 82 |
+ } |
|
| 80 | 83 |
} |
| 81 | 84 |
|
| 82 | 85 |
func buildEventsQueryParams(options EventsListOptions) (url.Values, error) {
|
| ... | ... |
@@ -9,18 +9,26 @@ import ( |
| 9 | 9 |
"github.com/moby/moby/api/types/system" |
| 10 | 10 |
) |
| 11 | 11 |
|
| 12 |
+type InfoOptions struct {
|
|
| 13 |
+ // No options currently; placeholder for future use |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+type SystemInfoResult struct {
|
|
| 17 |
+ Info system.Info |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 12 | 20 |
// Info returns information about the docker server. |
| 13 |
-func (cli *Client) Info(ctx context.Context) (system.Info, error) {
|
|
| 14 |
- var info system.Info |
|
| 21 |
+func (cli *Client) Info(ctx context.Context, options InfoOptions) (SystemInfoResult, error) {
|
|
| 15 | 22 |
resp, err := cli.get(ctx, "/info", url.Values{}, nil)
|
| 16 | 23 |
defer ensureReaderClosed(resp) |
| 17 | 24 |
if err != nil {
|
| 18 |
- return info, err |
|
| 25 |
+ return SystemInfoResult{}, err
|
|
| 19 | 26 |
} |
| 20 | 27 |
|
| 28 |
+ var info system.Info |
|
| 21 | 29 |
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
| 22 |
- return info, fmt.Errorf("Error reading remote info: %v", err)
|
|
| 30 |
+ return SystemInfoResult{}, fmt.Errorf("Error reading remote info: %v", err)
|
|
| 23 | 31 |
} |
| 24 | 32 |
|
| 25 |
- return info, nil |
|
| 33 |
+ return SystemInfoResult{Info: info}, nil
|
|
| 26 | 34 |
} |