Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit 144c3d2

Browse files
committed
Override Cassandra configuration and launch Cassandra directly
* Bypass the entrypoint script of the chosen Docker image. * Instead, make direct changes to the Cassandra.yaml file. * And execute ``cassandra`` directly. * Add a pilot integration test
1 parent c582e3f commit 144c3d2

File tree

23 files changed

+1900
-74
lines changed

23 files changed

+1900
-74
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
.generate_exes
77
.get_deps
88
bin/
9+
**/.test/

Makefile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ CMDS := controller apiserver pilot-elasticsearch pilot-cassandra
1818
GOPATH ?= /tmp/go
1919
GOFLAGS ?= "-a"
2020

21+
# Path of the Navigator Cassandra Pilot for integration tests
22+
TEST_ASSET_NAVIGATOR_PILOT_CASSANDRA ?= "${CURDIR}/navigator-pilot-cassandra_linux_amd64"
23+
2124
help:
2225
# all - runs verify, build and docker_build targets
2326
# test - runs go_test target
@@ -32,7 +35,7 @@ help:
3235

3336
all: verify build docker_build
3437

35-
test: go_test
38+
test: go_test test_integration
3639

3740
.run_e2e:
3841
${HACK_DIR}/prepare-e2e.sh
@@ -44,7 +47,7 @@ build: $(CMDS)
4447

4548
generate: .generate_files
4649

47-
verify: .hack_verify dep_verify go_verify helm_verify
50+
verify: .hack_verify dep_verify go_verify helm_verify test_integration
4851

4952
.hack_verify:
5053
@echo Running repo-infra verify scripts
@@ -90,7 +93,7 @@ $(CMDS):
9093
go_build: $(CMDS)
9194

9295
go_test:
93-
go test -v $$(go list ./... | grep -v '/vendor/')
96+
go test -v $$(go list ./... | grep -v -e '/vendor/' -e 'github.com/jetstack/navigator/test/')
9497

9598
go_fmt:
9699
./hack/verify-lint.sh
@@ -105,3 +108,11 @@ go_fmt:
105108
# Helm targets
106109
helm_verify:
107110
helm lint contrib/charts/*
111+
112+
.download_integration_test_binaries:
113+
$(HACK_DIR)/download-integration-test-binaries.sh
114+
touch .download_integration_test_binaries
115+
116+
test_integration: .download_integration_test_binaries apiserver pilot-cassandra
117+
TEST_ASSET_NAVIGATOR_PILOT_CASSANDRA=$(TEST_ASSET_NAVIGATOR_PILOT_CASSANDRA) \
118+
go test $(GO_TEST_ARGS) -v ./test/integration/...

docs/cassandra.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ All the C* nodes (pods) in a ``nodepool`` have the same configuration and the fo
2525

2626
.. include:: configure-scheduler.rst
2727

28+
.. _availability-zones-cassandra:
29+
2830
Cassandra Across Multiple Availability Zones
2931
--------------------------------------------
3032

@@ -240,6 +242,37 @@ Navigator will add C* nodes, one at a time, until the desired number of nodes is
240242

241243
You can look at ``CassandraCluster.Status.NodePools[<nodepoolname>].ReadyReplicas`` to see the current number of healthy C* nodes in each ``nodepool``.
242244

245+
Pilots and Cassandra Docker Images
246+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
247+
248+
By default, Navigator will use the `Cassandra Docker images from DockerHub <https://hub.docker.com/_/cassandra/>`_.
249+
It will use an image with a tag matching the supplied ``CassandraCluster.Spec.Version`` field.
250+
If you prefer to use your own container image you should configure the ``CassandraCluster.Spec.Image`` fields.
251+
252+
Navigator installs a ``navigator-pilot-cassandra`` executable into each Pod at the path ``/pilot``.
253+
This ``pilot`` process connects to the API server to:
254+
get extra configuration settings,
255+
report the status of this C* node, and to
256+
perform leader election of a single pilot in the cluster.
257+
258+
The ``pilot`` overrides the following keys in the default ``/etc/cassandra/cassandra.yaml`` file:
259+
260+
* ``cluster_name``: This will be set to match the name of the corresponding ``CassandraCluster`` resource in the API server.
261+
* ``listen_address`` / ``listen_interface`` / ``broadcast_address`` / ``rpc_address`` / ``broadcast_rpc_address``: These keys and the corresponding values will be removed from the default configuration.
262+
This ensures that Cassandra process listens and communicates using the IP address currently associated with the fully qualified domain name of the Pod.
263+
This is important if the Pod is moved to another node and is assigned a different IP address.
264+
Removing these settings from the Configuration file ensures that Cassandra uses the most recent IP address that Kubernetes has assigned to the Pod and that other C* nodes in the cluster are notified of the change of IP address.
265+
* ``seed_provider``: This is set to ``io.jetstack.cassandra.KubernetesSeedProvider`` which allows Cassandra to look up the seed IP addresses from a Kubernetes service.
266+
The ``seed_provider.*.seeds`` sub key will be removed.
267+
This is to avoid the risk of nodes mistakenly joining the cluster as seeds if the seed provider service is temporarily unavailable.
268+
269+
The ``pilot`` also overwrites ``cassandra-rackdc.properties`` with values derived from the ``CassandraCluster.Spec.Nodepools`` (see :ref:`availability-zones-cassandra`).
270+
271+
Finally the ``pilot`` executes ``/usr/bin/cassandra`` directly.
272+
273+
.. note::
274+
The default entry point (e.g. `/docker-entrypoint.sh <https://github.com/docker-library/cassandra/blob/master/3.11/docker-entrypoint.sh>`_ is ignored.
275+
243276
Supported Versions
244277
------------------
245278

docs/quick-start/cassandra-cluster.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ spec:
3131
pilotImage:
3232
repository: "quay.io/jetstack/navigator-pilot-cassandra"
3333
tag: "v0.1.0"
34+
securityContext:
35+
runAsUser: 999
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o nounset
5+
set -o pipefail
6+
set -o xtrace
7+
8+
# Close stdin
9+
exec 0<&-
10+
11+
ROOT_DIR="$(git rev-parse --show-toplevel)"
12+
13+
ETCD_VERSION=v3.2.10
14+
ETCD_URL="https://storage.googleapis.com/etcd"
15+
16+
KUBE_VERSION_URL="https://storage.googleapis.com/kubernetes-release/release/stable-1.10.txt"
17+
KUBE_VERSION=$(curl --fail --silent "${KUBE_VERSION_URL}")
18+
KUBE_BIN_URL="https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/bin/linux/amd64"
19+
20+
ASSETS_DIR="${ROOT_DIR}/vendor/sigs.k8s.io/testing_frameworks/integration/assets/bin"
21+
22+
mkdir -p "${ASSETS_DIR}"
23+
24+
curl --fail --silent ${ETCD_URL}/${ETCD_VERSION}/etcd-${ETCD_VERSION}-linux-amd64.tar.gz | \
25+
tar --extract --gzip --directory="${ASSETS_DIR}" --strip-components=1 '*/etcd'
26+
curl --fail --silent --output "${ASSETS_DIR}/kube-apiserver" "${KUBE_BIN_URL}/kube-apiserver"
27+
curl --fail --silent --output "${ASSETS_DIR}/kubectl" "${KUBE_BIN_URL}/kubectl"
28+
29+
chmod +x ${ASSETS_DIR}/*

hack/testdata/cass-cluster-test.template.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ spec:
2424
repository: "${NAVIGATOR_IMAGE_REPOSITORY}/navigator-pilot-cassandra"
2525
tag: "${NAVIGATOR_IMAGE_TAG}"
2626
pullPolicy: "${NAVIGATOR_IMAGE_PULLPOLICY}"
27+
securityContext:
28+
runAsUser: 999
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package testfs
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"path"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
type TestFs struct {
15+
t *testing.T
16+
d string
17+
}
18+
19+
func New(t *testing.T) *TestFs {
20+
d := fmt.Sprintf(".test/%s", t.Name())
21+
d, err := filepath.Abs(d)
22+
require.NoError(t, err)
23+
err = os.RemoveAll(d)
24+
if err != nil && !os.IsNotExist(err) {
25+
t.Fatalf("Error while removing old test directory: %s", err)
26+
}
27+
28+
err = os.MkdirAll(d, os.ModePerm)
29+
require.NoError(t, err)
30+
31+
return &TestFs{
32+
t: t,
33+
d: d,
34+
}
35+
}
36+
37+
func (tfs *TestFs) TempPath(name string) string {
38+
outPath := path.Join(tfs.d, name)
39+
tmpFile, err := ioutil.TempFile(tfs.d, name)
40+
require.NoError(tfs.t, err)
41+
err = tmpFile.Close()
42+
require.NoError(tfs.t, err)
43+
err = os.Rename(tmpFile.Name(), outPath)
44+
require.NoError(tfs.t, err)
45+
return outPath
46+
}
47+
48+
func (tfs *TestFs) TempDir(name string) string {
49+
outPath := path.Join(tfs.d, name)
50+
err := os.MkdirAll(outPath, os.ModePerm)
51+
require.NoError(tfs.t, err)
52+
return outPath
53+
}

pkg/config/config.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package config
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/pkg/errors"
8+
"github.com/spf13/viper"
9+
shutil "github.com/termie/go-shutil"
10+
)
11+
12+
const (
13+
BackupSuffix = ".navigator_original"
14+
)
15+
16+
type config struct {
17+
*viper.Viper
18+
}
19+
20+
type Interface interface {
21+
WriteConfig() error
22+
Set(key string, value interface{})
23+
AllSettings() map[string]interface{}
24+
}
25+
26+
var _ Interface = &config{}
27+
28+
func newFrom(path, format string) (Interface, error) {
29+
path, err := filepath.Abs(path)
30+
if err != nil {
31+
return nil, errors.Wrap(err, "unable to read absolute path")
32+
}
33+
backupPath := path + BackupSuffix
34+
err = shutil.CopyFile(path, backupPath, false)
35+
if err != nil && !os.IsNotExist(err) {
36+
return nil, errors.Wrap(err, "unable to create backup file")
37+
}
38+
c := &config{viper.New()}
39+
c.SetConfigFile(path)
40+
c.SetConfigType(format)
41+
f, err := os.Open(path)
42+
if err != nil && !os.IsNotExist(err) {
43+
return nil, errors.Wrap(err, "unable to open file")
44+
}
45+
defer f.Close()
46+
err = c.ReadConfig(f)
47+
if err != nil {
48+
return nil, errors.Wrap(err, "unable to read file")
49+
}
50+
return c, nil
51+
}
52+
53+
func NewFromYaml(path string) (Interface, error) {
54+
return newFrom(path, "yaml")
55+
}
56+
57+
func NewFromProperties(path string) (Interface, error) {
58+
return newFrom(path, "properties")
59+
}

pkg/config/config_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package config_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
shutil "github.com/termie/go-shutil"
9+
10+
"github.com/jetstack/navigator/internal/test/util/testfs"
11+
"github.com/jetstack/navigator/pkg/config"
12+
)
13+
14+
func TestRoundTrip(t *testing.T) {
15+
tfs := testfs.New(t)
16+
17+
inPath := tfs.TempPath("config.yaml")
18+
err := shutil.CopyFile("testdata/config1.yaml", inPath, false)
19+
require.NoError(t, err)
20+
21+
c1, err := config.NewFromYaml(inPath)
22+
require.NoError(t, err)
23+
24+
err = c1.WriteConfig()
25+
require.NoError(t, err)
26+
27+
c2, err := config.NewFromYaml(inPath)
28+
require.NoError(t, err)
29+
30+
assert.Equal(t, c1.AllSettings(), c2.AllSettings())
31+
}
32+
33+
func TestBackup(t *testing.T) {
34+
tfs := testfs.New(t)
35+
path := tfs.TempPath("conf1.yaml")
36+
_, err := config.NewFromYaml(path)
37+
require.NoError(t, err)
38+
assert.FileExists(t, path+".navigator_original")
39+
}
40+
41+
func TestNoSource(t *testing.T) {
42+
tfs := testfs.New(t)
43+
dir := tfs.TempDir("conf1")
44+
path := dir + "/conf1.yaml"
45+
_, err := config.NewFromYaml(path)
46+
require.NoError(t, err)
47+
}

pkg/config/testdata/config1.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foo:
2+
- bar
3+
- baz

0 commit comments

Comments
 (0)