Skip to content

Commit 26e309b

Browse files
committed
Merge branch 'develop' into feature/CLD-278
2 parents 0aff637 + 5a22f57 commit 26e309b

File tree

7 files changed

+318
-71
lines changed

7 files changed

+318
-71
lines changed

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# MarkLogic Kubernetes Helm Chart
22

3+
- [MarkLogic Kubernetes Helm Chart](#marklogic-kubernetes-helm-chart)
34
- [Introduction](#introduction)
45
- [Prerequisites](#prerequisites)
56
- [Set Up the Required Tools](#set-up-the-required-tools)
@@ -17,6 +18,13 @@
1718
- [Configuration Options](#configuration-options)
1819
- [--values](#--values)
1920
- [--set](#--set)
21+
- [Setting MarkLogic admin password](#setting-marklogic-admin-password)
22+
- [Log Collection](#log-collection)
23+
- [Adding and Removing Hosts from Clusters](#adding-and-removing-hosts-from-clusters)
24+
- [Adding Hosts](#adding-hosts)
25+
- [Removing Hosts](#removing-hosts)
26+
- [Enabling SSL over XDQP](#enabling-ssl-over-xdqp)
27+
- [Deploying a MarkLogic Cluster with Multiple Groups](#deploying-a-marklogic-cluster-with-multiple-groups)
2028
- [Access the MarkLogic Server](#access-the-marklogic-server)
2129
- [Service](#service)
2230
- [Get the ClusterIP Service Name](#get-the-clusterip-service-name)
@@ -221,6 +229,22 @@ helm install my-release marklogic/marklogic --version=1.0.0-ea1 \
221229

222230
We recommend that you use the `values.yaml` file for configuring your installation.
223231

232+
### Setting MarkLogic admin password
233+
234+
If the password does not provided when installing the MarkLogic Chart, a randomly generated aphanumeric value will be set for MarkLogic admin password. This value is stored in Kuberenetes secrets.
235+
User can also set a custom password by setting auth.adminPassword value during installation.
236+
To retrieve the randomly generated admin password, use the following commands:
237+
238+
1. List the secrets for MarkLogic deployment:
239+
```
240+
kubectl get secrets
241+
```
242+
Identify the name of the secret.
243+
244+
2. Save the secret name from step 1 and get the admin password using the following script:
245+
```
246+
kubectl get secret SECRET_NAME -o jsonpath='{.data.marklogic-password}' | base64 --decode
247+
```
224248
### Log Collection
225249

226250
To enable log collection for all Marklogic logs set logCollection.enabled to true. Set each option in logCollection.files to true of false depending on if you want to track each type of Marklogic log file.
@@ -272,13 +296,27 @@ kubectl logs pod/terminated-host-pod-name
272296
```
273297

274298
If you are permanently removing the host from the MarkLogic cluster, once the pod is terminated, follow standard MarkLogic administrative procedures using the administrative UI or APIs to remove the MarkLogic host from the cluster. Also, because Kubernetes keeps the Persistent Volume Claims and Persistent Volumes around until they are explicitly deleted, you must manually delete them using the Kubernetes APIs before attempting to scale the hosts in the StatefulSet back up again.
275-
276299
### Enabling SSL over XDQP
277300

278301
To enable SSL over XDQP, set the `enableXdqpSsl` to true either in the values.yaml file or using the `--set` flag. All communications to and from hosts in the cluster will be secured. When this setting is on, default SSL certificates will be used for XDQP encryption.
279302

280303
Note: To enable other XDQP/SSL settings like `xdqp ssl allow sslv3`, `xdqp ssl allow tls`, `xdqp ssl ciphers`, use MarkLogic REST Management API. See the MarkLogic documentation [here](https://docs.marklogic.com/REST/management).
281304

305+
# Deploying a MarkLogic Cluster with Multiple Groups
306+
307+
To deploy a MarkLogic cluster with multiple groups (separate E and D nodes for example) the `bootstrapHostName` and `group.name` must be configured in values.yaml or set the values provided for these configurations using the `--set` flag while installing helm charts.
308+
For example, if you want to create a MarkLogic cluster with three nodes in a "dnode" group and two nodes in an "enode" group, start with the following helm command:
309+
310+
```
311+
helm install dnode-group ./charts/ --set group.name=dnode --set replicaCount=3
312+
```
313+
Once this deployment is complete, a MarkLogic cluster with three hosts should be running.
314+
To add the "enode" group and nodes to the cluster, the `bootstrapHostName` must be set to join the existing MarkLogic cluster. The first host in the other group can be used. For this example, set `bootstrapHostName` to `dnode-group-marklogic-0.dnode-group-marklogic-headless.default.svc.cluster.local` with the following command:
315+
316+
```
317+
helm install enode-group ./charts/ --set group.name=enode --set replicaCount=2 --set bootstrapHostName=dnode-group-marklogic-0.dnode-group-marklogic-headless.default.svc.cluster.local
318+
```
319+
Once this deployment is complete, there will be a new "enode" group with two hosts in the MarkLogic cluster.
282320

283321
# Access the MarkLogic Server
284322

charts/templates/secret.yaml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
{{- $adminPassword := (default (randAlphaNum 10) .Values.auth.adminPassword) | b64enc | quote }}
2+
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (printf "%s-admin" (include "marklogic.fullname" .))) }}
3+
{{- if $secret }}
4+
{{- $adminPassword = index $secret.data "password" }}
5+
{{- end }}
6+
17
apiVersion: v1
28
kind: Secret
39
metadata:
4-
name: {{ include "marklogic.fullname" . }}
10+
name: {{ include "marklogic.fullname" . }}-admin
511
namespace: {{ .Release.Namespace }}
612
labels:
713
{{- include "marklogic.labels" . | nindent 4 }}
814
type: kubernetes.io/basic-auth
9-
stringData:
10-
username: {{ .Values.auth.adminUsername}}
11-
password: {{ .Values.auth.adminPassword}}
15+
data:
16+
password: {{ $adminPassword }}
17+
username: {{ .Values.auth.adminUsername | b64enc | quote }}
18+

charts/templates/statefulset.yaml

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,31 @@ spec:
5151
exit 0
5252
fi
5353
log "Info: [initContainer] Group $MARKLOGIC_GROUP does not exist, configuring group $MARKLOGIC_GROUP on the MarkLogic cluster."
54-
for i in $(seq 1 5); do
55-
res_code=`curl --anyauth --user ${MARKLOGIC_ADMIN_USERNAME}:${MARKLOGIC_ADMIN_PASSWORD} -m 20 -s -w '%{http_code}' -X POST -d "${GROUP_CFG}" -H "Content-type: application/json" http://${MARKLOGIC_BOOTSTRAP_HOST}:8002/manage/v2/groups`
56-
if [[ ${res_code} -eq 201 ]]; then
57-
log "Info: [initContainer] Successfully configured group $MARKLOGIC_GROUP on the MarkLogic cluster."
58-
break
59-
else
60-
log "Info: [initContainer] Configure group $MARKLOGIC_GROUP retry attempt $i "
61-
log "Info: [initContainer] Expected response code 202, got $res_code"
62-
sleep 10s
63-
fi
64-
[ $i -eq 5 ] && exit 1
65-
done
54+
res_code=`curl --retry 5 --retry-all-errors --retry-max-time 60 --anyauth --user ${MARKLOGIC_ADMIN_USERNAME}:${MARKLOGIC_ADMIN_PASSWORD} -m 20 -s -w '%{http_code}' -X POST -d "${GROUP_CFG}" -H "Content-type: application/json" http://${MARKLOGIC_BOOTSTRAP_HOST}:8002/manage/v2/groups`
55+
if [[ ${res_code} -eq 201 ]]; then
56+
log "Info: [initContainer] Successfully configured group $MARKLOGIC_GROUP on the MarkLogic cluster."
57+
else
58+
log "Info: [initContainer] Expected response code 201, got $res_code"
59+
exit 1
60+
fi
61+
log "Info: [initContainer] Group $MARKLOGIC_GROUP has been created, configuring App-server App-Services in group $MARKLOGIC_GROUP on the MarkLogic cluster."
62+
res_code=`curl --retry 5 --retry-all-errors --retry-max-time 60 --anyauth --user ${MARKLOGIC_ADMIN_USERNAME}:${MARKLOGIC_ADMIN_PASSWORD} -m 20 -s -w '%{http_code}' -X POST -d '{"server-name":"App-Services", "root":"/", "port":8000,"modules-database":"Modules", "content-database":"Documents", "error-handler":"/MarkLogic/rest-api/8000-error-handler.xqy", "url-rewriter":"/MarkLogic/rest-api/8000-rewriter.xml"}' -H "Content-type: application/json" "http://${MARKLOGIC_BOOTSTRAP_HOST}:8002/manage/v2/servers?group-id=${MARKLOGIC_GROUP}&server-type=http"`
63+
if [[ ${res_code} -eq 201 ]]; then
64+
log "Info: [initContainer] Successfully configured App-server App-Services into group $MARKLOGIC_GROUP on the MarkLogic cluster."
65+
else
66+
log "Info: [initContainer] Expected response code 201, got $res_code"
67+
exit 1
68+
fi
6669
env:
6770
- name: MARKLOGIC_ADMIN_USERNAME
6871
valueFrom:
6972
secretKeyRef:
70-
name: {{ include "marklogic.fullname" . }}
73+
name: {{ include "marklogic.fullname" . }}-admin
7174
key: username
7275
- name: MARKLOGIC_ADMIN_PASSWORD
7376
valueFrom:
7477
secretKeyRef:
75-
name: {{ include "marklogic.fullname" . }}
78+
name: {{ include "marklogic.fullname" . }}-admin
7679
key: password
7780
- name: POD_NAME
7881
valueFrom:
@@ -95,13 +98,13 @@ spec:
9598
env:
9699
- name: MARKLOGIC_ADMIN_USERNAME
97100
valueFrom:
98-
secretKeyRef:
99-
name: {{ include "marklogic.fullname" . }}
100-
key: username
101+
secretKeyRef:
102+
name: {{ include "marklogic.fullname" . }}-admin
103+
key: username
101104
- name: MARKLOGIC_ADMIN_PASSWORD
102105
valueFrom:
103106
secretKeyRef:
104-
name: {{ include "marklogic.fullname" . }}
107+
name: {{ include "marklogic.fullname" . }}-admin
105108
key: password
106109
- name: POD_NAME
107110
valueFrom:

charts/values.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fullnameOverride: ""
5151
# Configure Marklogic Admin Username and Password
5252
auth:
5353
adminUsername: admin
54-
adminPassword: admin
54+
adminPassword: ""
5555

5656
# Configure Affinity property for scheduling pods to nodes
5757
# ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
@@ -137,7 +137,7 @@ startupProbe:
137137
# Log collection will collect all logs for each file type enabled, parse them,
138138
# And export them to a logging backend specified in the outputs section below
139139
logCollection:
140-
enabled: true
140+
enabled: false
141141
image: fluent/fluent-bit:1.9.7
142142
resources:
143143
requests:

test/e2e/install_test.go

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package e2e
33
import (
44
"crypto/tls"
55
"fmt"
6+
"io/ioutil"
7+
"net/http"
68
"os"
79
"path/filepath"
810
"strings"
@@ -13,6 +15,9 @@ import (
1315
http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
1416
"github.com/gruntwork-io/terratest/modules/k8s"
1517
"github.com/gruntwork-io/terratest/modules/random"
18+
"github.com/stretchr/testify/assert"
19+
"github.com/tidwall/gjson"
20+
digestAuth "github.com/xinsnake/go-http-digest-auth-client"
1621
)
1722

1823
func TestHelmInstall(t *testing.T) {
@@ -23,6 +28,9 @@ func TestHelmInstall(t *testing.T) {
2328
}
2429
imageRepo, repoPres := os.LookupEnv("dockerRepository")
2530
imageTag, tagPres := os.LookupEnv("dockerVersion")
31+
var resp *http.Response
32+
var body []byte
33+
var err error
2634

2735
if !repoPres {
2836
imageRepo = "marklogic-centos/marklogic-server-centos"
@@ -40,7 +48,7 @@ func TestHelmInstall(t *testing.T) {
4048
KubectlOptions: kubectlOptions,
4149
SetValues: map[string]string{
4250
"persistence.enabled": "false",
43-
"replicaCount": "1",
51+
"replicaCount": "2",
4452
"image.repository": imageRepo,
4553
"image.tag": imageTag,
4654
"logCollection.enabled": "false",
@@ -61,21 +69,65 @@ func TestHelmInstall(t *testing.T) {
6169
podName := releaseName + "-marklogic-0"
6270
// wait until the pod is in Ready status
6371
k8s.WaitUntilPodAvailable(t, kubectlOptions, podName, 10, 15*time.Second)
64-
tunnel := k8s.NewTunnel(
65-
kubectlOptions, k8s.ResourceTypePod, podName, 7997, 7997)
66-
defer tunnel.Close()
67-
tunnel.ForwardPort(t)
68-
endpoint := fmt.Sprintf("http://%s", tunnel.Endpoint())
69-
t.Logf(`Endpoint: %s`, endpoint)
72+
tunnel7997 := k8s.NewTunnel(kubectlOptions, k8s.ResourceTypePod, podName, 7997, 7997)
73+
defer tunnel7997.Close()
74+
tunnel7997.ForwardPort(t)
75+
endpoint7997 := fmt.Sprintf("http://%s", tunnel7997.Endpoint())
7076

77+
// verify if 7997 health check endpoint returns 200
7178
http_helper.HttpGetWithRetryWithCustomValidation(
7279
t,
73-
endpoint,
80+
endpoint7997,
7481
&tlsConfig,
7582
10,
7683
15*time.Second,
7784
func(statusCode int, body string) bool {
7885
return statusCode == 200
7986
},
8087
)
88+
89+
t.Log("====Testing Generated Random Password====")
90+
secretName := releaseName + "-marklogic-admin"
91+
secret := k8s.GetSecret(t, kubectlOptions, secretName)
92+
passwordArr := secret.Data["password"]
93+
password := string(passwordArr[:])
94+
// the generated random password should have length of 10
95+
assert.Equal(t, 10, len(password))
96+
usernameArr := secret.Data["username"]
97+
username := string(usernameArr[:])
98+
expectedUsername := "admin"
99+
// the username from secret expected to be "admin"
100+
assert.Equal(t, expectedUsername, username)
101+
102+
tunnel8002 := k8s.NewTunnel(kubectlOptions, k8s.ResourceTypePod, podName, 8002, 8002)
103+
defer tunnel8002.Close()
104+
tunnel8002.ForwardPort(t)
105+
endpointManage := fmt.Sprintf("http://%s/manage/v2", tunnel8002.Endpoint())
106+
107+
request := digestAuth.NewRequest(username, password, "GET", endpointManage, "")
108+
response, err := request.Execute()
109+
if err != nil {
110+
t.Fatalf(err.Error())
111+
}
112+
defer response.Body.Close()
113+
// the generated password should be able to access the manage endpoint
114+
assert.Equal(t, 200, response.StatusCode)
115+
116+
t.Log("====Verify no groups beyond enode were created/modified====")
117+
groupStatusEndpoint := fmt.Sprintf("http://%s/manage/v2/groups?format=json", tunnel8002.Endpoint())
118+
groupStatus := digestAuth.NewRequest(username, password, "GET", groupStatusEndpoint, "")
119+
t.Logf(`groupStatusEndpoint: %s`, groupStatusEndpoint)
120+
if resp, err = groupStatus.Execute(); err != nil {
121+
t.Fatalf(err.Error())
122+
}
123+
if body, err = ioutil.ReadAll(resp.Body); err != nil {
124+
t.Fatalf(err.Error())
125+
}
126+
groupQuantityJSON := gjson.Get(string(body), "group-default-list.list-items.list-count.value")
127+
128+
if groupQuantityJSON.Num != 1 {
129+
t.Errorf("Only one group should exist, instead %v groups exist", groupQuantityJSON.Num)
130+
}
131+
132+
t.Logf("Groups status response:\n" + string(body))
81133
}

0 commit comments

Comments
 (0)