Skip to content

Commit 92afe45

Browse files
authored
[Feature] Early connections support (#1102)
1 parent a4e26f3 commit 92afe45

14 files changed

+623
-97
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A)
44
- (Feature) Add new field to DeploymentReplicationStatus with details on DC2DC sync status
5+
- (Feature) Early connections support
56

67
## [1.2.16](https://github.com/arangodb/kube-arangodb/tree/1.2.16) (2022-09-14)
78
- (Feature) Add ArangoDeployment ServerGroupStatus

cmd/lifecycle_probes.go

Lines changed: 96 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,80 @@ package cmd
2222

2323
import (
2424
"crypto/tls"
25+
"encoding/json"
2526
"fmt"
2627
"io/ioutil"
2728
"net/http"
2829
"os"
2930
"path"
31+
"strconv"
3032

3133
"github.com/pkg/errors"
3234
"github.com/rs/zerolog/log"
3335
"github.com/spf13/cobra"
3436

37+
"github.com/arangodb/go-driver"
3538
"github.com/arangodb/go-driver/jwt"
3639

40+
api "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
3741
"github.com/arangodb/kube-arangodb/pkg/apis/shared"
42+
"github.com/arangodb/kube-arangodb/pkg/deployment/client"
43+
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
3844
"github.com/arangodb/kube-arangodb/pkg/deployment/pod"
45+
"github.com/arangodb/kube-arangodb/pkg/deployment/resources"
3946
"github.com/arangodb/kube-arangodb/pkg/util/constants"
4047
)
4148

4249
var (
4350
cmdLifecycleProbe = &cobra.Command{
4451
Use: "probe",
45-
Run: cmdLifecycleProbeCheck,
52+
Run: cmdLifecycleProbeRun,
53+
}
54+
cmdLifecycleProbeLiveness = &cobra.Command{
55+
Use: "liveness",
56+
Run: cmdLifecycleProbeRun,
57+
}
58+
cmdLifecycleProbeReadiness = &cobra.Command{
59+
Use: "readiness",
60+
Run: cmdLifecycleProbeRun,
61+
}
62+
cmdLifecycleProbeStartUp = &cobra.Command{
63+
Use: "startup",
64+
Run: cmdLifecycleProbeRun,
4665
}
4766

4867
probeInput struct {
49-
SSL bool
50-
Auth bool
51-
Endpoint string
52-
JWTPath string
68+
Endpoint string
69+
JWTPath string
70+
ArangoDBVersion string
71+
ServerGroup string
72+
DeploymentMode string
73+
SSL bool
74+
Auth bool
75+
Enterprise bool
5376
}
5477
)
5578

5679
func init() {
5780
f := cmdLifecycleProbe.PersistentFlags()
5881

82+
cmdLifecycleProbe.AddCommand(cmdLifecycleProbeLiveness)
83+
cmdLifecycleProbe.AddCommand(cmdLifecycleProbeReadiness)
84+
cmdLifecycleProbe.AddCommand(cmdLifecycleProbeStartUp)
85+
5986
f.BoolVarP(&probeInput.SSL, "ssl", "", false, "Determines if SSL is enabled")
6087
f.BoolVarP(&probeInput.Auth, "auth", "", false, "Determines if authentication is enabled")
61-
f.StringVarP(&probeInput.Endpoint, "endpoint", "", "/_api/version", "Endpoint (path) to call for lifecycle probe")
88+
f.StringVarP(&probeInput.Endpoint, "endpoint", "", client.ServerApiVersionEndpoint, "Endpoint (path) to call for lifecycle probe")
89+
f.MarkDeprecated("endpoint", "Endpoint is chosen automatically by the lifecycle process")
6290
f.StringVarP(&probeInput.JWTPath, "jwt", "", shared.ClusterJWTSecretVolumeMountDir, "Path to the JWT tokens")
91+
f.StringVar(&probeInput.ArangoDBVersion, "arangodb-version", os.Getenv(resources.ArangoDBOverrideVersionEnv),
92+
"Version of the ArangoDB")
93+
f.StringVar(&probeInput.ServerGroup, "serverGroup", os.Getenv(resources.ArangoDBOverrideServerGroupEnv),
94+
"Name of the group where a server belongs to")
95+
f.StringVar(&probeInput.DeploymentMode, "deploymentMode", os.Getenv(resources.ArangoDBOverrideDeploymentModeEnv),
96+
"A deployment mode (Cluster, Single, ActiveFailover)")
97+
enterprise, _ := strconv.ParseBool(os.Getenv(resources.ArangoDBOverrideEnterpriseEnv))
98+
f.BoolVar(&probeInput.Enterprise, "enterprise", enterprise, "Determines if ArangoDB is enterprise")
6399
}
64100

65101
func probeClient() *http.Client {
@@ -147,10 +183,10 @@ func addAuthHeader(req *http.Request) error {
147183
return nil
148184
}
149185

150-
func doRequest() (*http.Response, error) {
186+
func doRequest(endpoint string) (*http.Response, error) {
151187
client := probeClient()
152188

153-
req, err := http.NewRequest(http.MethodGet, probeEndpoint(probeInput.Endpoint), nil)
189+
req, err := http.NewRequest(http.MethodGet, probeEndpoint(endpoint), nil)
154190
if err != nil {
155191
return nil, err
156192
}
@@ -162,22 +198,25 @@ func doRequest() (*http.Response, error) {
162198
return client.Do(req)
163199
}
164200

165-
func cmdLifecycleProbeCheck(cmd *cobra.Command, args []string) {
166-
if err := cmdLifecycleProbeCheckE(); err != nil {
201+
func cmdLifecycleProbeRun(cmd *cobra.Command, _ []string) {
202+
if err := cmdLifecycleProbeRunE(cmd); err != nil {
167203
log.Error().Err(err).Msgf("Fatal")
168204
os.Exit(1)
169205
}
170206
}
171207

172-
func cmdLifecycleProbeCheckE() error {
173-
resp, err := doRequest()
208+
func cmdLifecycleProbeRunE(cmd *cobra.Command) error {
209+
endpoint := getEndpoint(api.ProbeType(cmd.Use))
210+
resp, err := doRequest(endpoint)
174211
if err != nil {
175212
return err
176213
}
214+
if resp.Body != nil {
215+
defer resp.Body.Close()
216+
}
177217

178218
if resp.StatusCode != http.StatusOK {
179219
if resp.Body != nil {
180-
defer resp.Body.Close()
181220
if data, err := ioutil.ReadAll(resp.Body); err == nil {
182221
return errors.Errorf("Unexpected code: %d - %s", resp.StatusCode, string(data))
183222
}
@@ -186,7 +225,51 @@ func cmdLifecycleProbeCheckE() error {
186225
return errors.Errorf("Unexpected code: %d", resp.StatusCode)
187226
}
188227

228+
if endpoint == client.ServerStatusEndpoint {
229+
// When server status endpoint is used then HTTP status code 200 is not enough.
230+
// The progress should be also checked.
231+
if resp.Body == nil {
232+
return errors.Errorf("Expected body from the \"%s\" endpoint", endpoint)
233+
}
234+
data, err := ioutil.ReadAll(resp.Body)
235+
if err != nil {
236+
return errors.Errorf("Failed to read body from the \"%s\" endpoint", endpoint)
237+
}
238+
239+
status := client.ServerStatus{}
240+
if err = json.Unmarshal(data, &status); err != nil {
241+
return errors.Errorf("Failed to unmarshal %s into server status", string(data))
242+
}
243+
244+
if progress, ok := status.GetProgress(); !ok {
245+
return errors.Errorf("server not ready: %s", progress)
246+
}
247+
}
248+
189249
log.Info().Msgf("Check passed")
190250

191251
return nil
192252
}
253+
254+
// getEndpoint returns endpoint to the ArangoDB instance where readiness should be checked.
255+
func getEndpoint(probeType api.ProbeType) string {
256+
if probeType == api.ProbeTypeReadiness {
257+
if probeInput.DeploymentMode == string(api.DeploymentModeActiveFailover) {
258+
v := driver.Version(probeInput.ArangoDBVersion)
259+
if features.FailoverLeadership().Supported(v, probeInput.Enterprise) {
260+
return client.ServerApiVersionEndpoint
261+
}
262+
}
263+
264+
return client.ServerAvailabilityEndpoint
265+
}
266+
267+
if probeInput.ServerGroup == api.ServerGroupDBServersString {
268+
v := driver.Version(probeInput.ArangoDBVersion)
269+
if features.Version310().Supported(v, probeInput.Enterprise) {
270+
return client.ServerStatusEndpoint
271+
}
272+
}
273+
274+
return client.ServerApiVersionEndpoint
275+
}

pkg/apis/deployment/v1/probes.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 v1
22+
23+
type ProbeType string
24+
25+
const (
26+
ProbeTypeLiveness = "liveness"
27+
ProbeTypeReadiness = "readiness"
28+
ProbeTypeStartUp = "startup"
29+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 v2alpha1
22+
23+
type ProbeType string
24+
25+
const (
26+
ProbeTypeLiveness = "liveness"
27+
ProbeTypeReadiness = "readiness"
28+
ProbeTypeStartUp = "startup"
29+
)

pkg/deployment/client/status.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 client
22+
23+
import (
24+
"fmt"
25+
"strings"
26+
)
27+
28+
const (
29+
// ServerProgressPhaseInWait describes success progress state of a server.
30+
ServerProgressPhaseInWait = "in wait"
31+
// ServerStatusEndpoint describes endpoint of a server status.
32+
ServerStatusEndpoint = "/_admin/status"
33+
// ServerApiVersionEndpoint describes endpoint of a server version.
34+
ServerApiVersionEndpoint = "/_api/version"
35+
// ServerAvailabilityEndpoint describes endpoint of a server availability.
36+
ServerAvailabilityEndpoint = "/_admin/server/availability"
37+
)
38+
39+
// ServerProgress describes server progress.
40+
type ServerProgress struct {
41+
// Phase is a name of the lifecycle phase the instance is currently in.
42+
Phase string `json:"phase,omitempty"`
43+
// Feature is internal name of the feature that is currently being prepared
44+
Feature string `json:"feature,omitempty"`
45+
// Current recovery sequence number value, if the instance is currently recovering.
46+
// If the instance is already past the recovery, this attribute contains the last handled recovery sequence number.
47+
RecoveryTick int `json:"recoveryTick,omitempty"`
48+
}
49+
50+
// ServerInfo describes server information.
51+
type ServerInfo struct {
52+
ServerProgress ServerProgress `json:"progress,omitempty"`
53+
}
54+
55+
// ServerStatus describes server status.
56+
type ServerStatus struct {
57+
ServerInfo ServerInfo `json:"serverInfo,omitempty"`
58+
}
59+
60+
// GetProgress returns human-readable progress status of the server, and true if server is ready.
61+
func (s ServerStatus) GetProgress() (string, bool) {
62+
p := s.ServerInfo.ServerProgress
63+
var result strings.Builder
64+
65+
if len(p.Feature) > 0 {
66+
result.WriteString("feature: " + p.Feature + ", ")
67+
}
68+
69+
result.WriteString("phase: " + p.Phase)
70+
71+
if p.RecoveryTick > 0 {
72+
result.WriteString(", recoveryTick: " + fmt.Sprintf("%d", p.RecoveryTick))
73+
}
74+
75+
return result.String(), p.Phase == ServerProgressPhaseInWait
76+
}

pkg/deployment/deployment_encryption_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
5252
firstAgentStatus,
5353
},
5454
},
55-
Images: createTestImagesWithVersion(false, "3.7.0"),
55+
Images: createTestImagesWithVersion(false, testVersion),
5656
}
5757

5858
testCase.createTestPodData(deployment, api.ServerGroupAgents, firstAgentStatus)
@@ -117,7 +117,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
117117
firstDBServerStatus,
118118
},
119119
},
120-
Images: createTestImagesWithVersion(false, "3.7.0"),
120+
Images: createTestImagesWithVersion(false, testVersion),
121121
}
122122

123123
testCase.createTestPodData(deployment, api.ServerGroupDBServers, firstDBServerStatus)
@@ -200,7 +200,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
200200
firstAgentStatus,
201201
},
202202
},
203-
Images: createTestImagesWithVersion(true, "3.7.0"),
203+
Images: createTestImagesWithVersion(true, testVersion),
204204
}
205205

206206
testCase.createTestPodData(deployment, api.ServerGroupAgents, firstAgentStatus)
@@ -263,7 +263,7 @@ func TestEnsurePod_ArangoDB_Encryption(t *testing.T) {
263263
firstAgentStatus,
264264
},
265265
},
266-
Images: createTestImagesWithVersion(true, "3.7.0"),
266+
Images: createTestImagesWithVersion(true, testVersion),
267267
}
268268

269269
testCase.createTestPodData(deployment, api.ServerGroupAgents, firstAgentStatus)

0 commit comments

Comments
 (0)