Skip to content

Commit 3191f5e

Browse files
authored
[Feature] Async Requests Support (#981)
1 parent aad986f commit 3191f5e

File tree

12 files changed

+725
-8
lines changed

12 files changed

+725
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- (Feature) Recursive OwnerReference discovery
88
- (Maintenance) Add check make targets
99
- (Feature) Create support for local variables in actions.
10+
- (Feature) Support for asynchronous ArangoD resquests.
1011

1112
## [1.2.11](https://github.com/arangodb/kube-arangodb/tree/1.2.11) (2022-04-30)
1213
- (Bugfix) Orphan PVC are not removed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ ifdef VERBOSE
150150
TESTVERBOSEOPTIONS := -v
151151
endif
152152

153-
EXCLUDE_DIRS := tests vendor .gobuild deps tools
153+
EXCLUDE_DIRS := vendor .gobuild deps tools
154154
SOURCES_QUERY := find ./ -type f -name '*.go' $(foreach EXCLUDE_DIR,$(EXCLUDE_DIRS), ! -path "*/$(EXCLUDE_DIR)/*")
155155
SOURCES := $(shell $(SOURCES_QUERY))
156156
DASHBOARDSOURCES := $(shell find $(DASHBOARDDIR)/src -name '*.js') $(DASHBOARDDIR)/package.json

pkg/util/arangod/conn/async.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package conn
22+
23+
import (
24+
"context"
25+
"net/http"
26+
"path"
27+
28+
"github.com/arangodb/go-driver"
29+
"github.com/arangodb/kube-arangodb/pkg/util/constants"
30+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
31+
)
32+
33+
func NewAsyncConnection(c driver.Connection) driver.Connection {
34+
return async{
35+
connectionPass: connectionPass{
36+
c: c,
37+
wrap: asyncConnectionWrap,
38+
},
39+
}
40+
}
41+
42+
func asyncConnectionWrap(c driver.Connection) (driver.Connection, error) {
43+
return NewAsyncConnection(c), nil
44+
}
45+
46+
type async struct {
47+
connectionPass
48+
}
49+
50+
func (a async) isAsyncIDSet(ctx context.Context) (string, bool) {
51+
if ctx != nil {
52+
if q := ctx.Value(asyncOperatorContextKey); q != nil {
53+
if v, ok := q.(string); ok {
54+
return v, true
55+
}
56+
}
57+
}
58+
59+
return "", false
60+
}
61+
62+
func (a async) Do(ctx context.Context, req driver.Request) (driver.Response, error) {
63+
if id, ok := a.isAsyncIDSet(ctx); ok {
64+
// We have ID Set, request should be done to fetch job id
65+
req, err := a.c.NewRequest(http.MethodPut, path.Join("/_api/job", id))
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
resp, err := a.c.Do(ctx, req)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
switch resp.StatusCode() {
76+
case http.StatusNotFound:
77+
return nil, newAsyncErrorNotFound(id)
78+
case http.StatusNoContent:
79+
return nil, newAsyncJobInProgress(id)
80+
default:
81+
return resp, nil
82+
}
83+
} else {
84+
req.SetHeader(constants.ArangoHeaderAsyncKey, constants.ArangoHeaderAsyncValue)
85+
86+
resp, err := a.c.Do(ctx, req)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
switch resp.StatusCode() {
92+
case http.StatusAccepted:
93+
if v := resp.Header(constants.ArangoHeaderAsyncIDKey); len(v) == 0 {
94+
return nil, errors.Newf("Missing async key response")
95+
} else {
96+
return nil, newAsyncJobInProgress(v)
97+
}
98+
default:
99+
return nil, resp.CheckStatus(http.StatusAccepted)
100+
}
101+
}
102+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package conn
22+
23+
import "fmt"
24+
25+
func IsAsyncErrorNotFound(err error) bool {
26+
if err == nil {
27+
return false
28+
}
29+
30+
if _, ok := err.(asyncErrorNotFound); ok {
31+
return true
32+
}
33+
34+
return false
35+
}
36+
37+
func newAsyncErrorNotFound(id string) error {
38+
return asyncErrorNotFound{
39+
jobID: id,
40+
}
41+
}
42+
43+
type asyncErrorNotFound struct {
44+
jobID string
45+
}
46+
47+
func (a asyncErrorNotFound) Error() string {
48+
return fmt.Sprintf("Job with ID %s not found", a.jobID)
49+
}
50+
51+
func IsAsyncJobInProgress(err error) (string, bool) {
52+
if err == nil {
53+
return "", false
54+
}
55+
56+
if v, ok := err.(asyncJobInProgress); ok {
57+
return v.jobID, true
58+
}
59+
60+
return "", false
61+
}
62+
63+
func newAsyncJobInProgress(id string) error {
64+
return asyncJobInProgress{
65+
jobID: id,
66+
}
67+
}
68+
69+
type asyncJobInProgress struct {
70+
jobID string
71+
}
72+
73+
func (a asyncJobInProgress) Error() string {
74+
return fmt.Sprintf("Job with ID %s in progress", a.jobID)
75+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package conn
22+
23+
import (
24+
"context"
25+
"net/http"
26+
"testing"
27+
28+
"github.com/arangodb/go-driver"
29+
"github.com/arangodb/kube-arangodb/pkg/util/tests"
30+
"github.com/stretchr/testify/require"
31+
)
32+
33+
func Test_Async(t *testing.T) {
34+
s := tests.NewServer(t)
35+
36+
c := s.NewConnection()
37+
38+
c = NewAsyncConnection(c)
39+
40+
client, err := driver.NewClient(driver.ClientConfig{
41+
Connection: c,
42+
})
43+
require.NoError(t, err)
44+
45+
a := tests.NewAsyncHandler(t, s, http.MethodGet, "/_api/version", http.StatusOK, driver.VersionInfo{
46+
Server: "foo",
47+
Version: "",
48+
License: "",
49+
Details: nil,
50+
})
51+
52+
a.Start()
53+
54+
_, err = client.Version(context.Background())
55+
require.Error(t, err)
56+
id, ok := IsAsyncJobInProgress(err)
57+
require.True(t, ok)
58+
require.Equal(t, a.ID(), id)
59+
60+
a.InProgress()
61+
62+
ctx := WithAsyncID(context.TODO(), a.ID())
63+
64+
_, err = client.Version(ctx)
65+
require.Error(t, err)
66+
id, ok = IsAsyncJobInProgress(err)
67+
require.True(t, ok)
68+
require.Equal(t, a.ID(), id)
69+
70+
a.Done()
71+
72+
v, err := client.Version(ctx)
73+
require.NoError(t, err)
74+
75+
require.Equal(t, v.Server, "foo")
76+
77+
defer s.Stop()
78+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package conn
22+
23+
import (
24+
"context"
25+
26+
"github.com/arangodb/go-driver"
27+
)
28+
29+
type connectionWrap func(c driver.Connection) (driver.Connection, error)
30+
31+
var _ driver.Connection = connectionPass{}
32+
33+
type connectionPass struct {
34+
c driver.Connection
35+
wrap connectionWrap
36+
}
37+
38+
func (c connectionPass) NewRequest(method, path string) (driver.Request, error) {
39+
return c.c.NewRequest(method, path)
40+
}
41+
42+
func (c connectionPass) Do(ctx context.Context, req driver.Request) (driver.Response, error) {
43+
return c.c.Do(ctx, req)
44+
}
45+
46+
func (c connectionPass) Unmarshal(data driver.RawObject, result interface{}) error {
47+
return c.c.Unmarshal(data, result)
48+
}
49+
50+
func (c connectionPass) Endpoints() []string {
51+
return c.c.Endpoints()
52+
}
53+
54+
func (c connectionPass) UpdateEndpoints(endpoints []string) error {
55+
return c.c.UpdateEndpoints(endpoints)
56+
}
57+
58+
func (c connectionPass) SetAuthentication(authentication driver.Authentication) (driver.Connection, error) {
59+
newC, err := c.c.SetAuthentication(authentication)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
if f := c.wrap; f != nil {
65+
return f(newC)
66+
}
67+
return newC, nil
68+
}
69+
70+
func (c connectionPass) Protocols() driver.ProtocolSet {
71+
return c.c.Protocols()
72+
}

pkg/util/arangod/conn/context.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package conn
22+
23+
import "context"
24+
25+
type ContextKey string
26+
27+
const (
28+
asyncOperatorContextKey ContextKey = "operator-async-id"
29+
)
30+
31+
func WithAsyncID(ctx context.Context, id string) context.Context {
32+
if ctx == nil {
33+
ctx = context.Background()
34+
}
35+
36+
return context.WithValue(ctx, asyncOperatorContextKey, id)
37+
}

0 commit comments

Comments
 (0)