diff --git a/pkg/storage/elasticsearch.go b/pkg/storage/elasticsearch.go index cd68a3a2d..169897942 100644 --- a/pkg/storage/elasticsearch.go +++ b/pkg/storage/elasticsearch.go @@ -1,6 +1,7 @@ package storage import ( + "fmt" "strings" "k8s.io/api/core/v1" @@ -49,6 +50,15 @@ func (ed *ElasticsearchDeployment) InjectStorageConfiguration(p *v1.PodSpec) { "--es.server-urls="+elasticsearchUrl, "--es.token-file="+k8sTokenFile, "--es.tls.ca="+caPath) + if !containsPrefix("--es.num-shards", p.Containers[0].Args) { + // taken from https://github.com/openshift/cluster-logging-operator/blob/32b69e8bcf61a805e8f3c45c664a3c08d1ee62d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/configmaps.go#L38 + // every ES node is a data node + p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es.num-shards=%d", ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)) + } + if !containsPrefix("--es.num-replicas", p.Containers[0].Args) { + p.Containers[0].Args = append(p.Containers[0].Args, fmt.Sprintf("--es.num-replicas=%d", + calculateReplicaShards(ed.Jaeger.Spec.Storage.Elasticsearch.RedundancyPolicy, int(ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount)))) + } p.Containers[0].VolumeMounts = append(p.Containers[0].VolumeMounts, v1.VolumeMount{ Name: volumeName, ReadOnly: true, @@ -97,14 +107,59 @@ func (ed *ElasticsearchDeployment) createCr() *esv1alpha1.Elasticsearch { Spec: esv1alpha1.ElasticsearchNodeSpec{ Resources: ed.Jaeger.Spec.Storage.Elasticsearch.Resources, }, - Nodes: []esv1alpha1.ElasticsearchNode{ - { - NodeCount: ed.Jaeger.Spec.Storage.Elasticsearch.NodeCount, - Storage: ed.Jaeger.Spec.Storage.Elasticsearch.Storage, - NodeSelector: ed.Jaeger.Spec.Storage.Elasticsearch.NodeSelector, - Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleClient, esv1alpha1.ElasticsearchRoleData, esv1alpha1.ElasticsearchRoleMaster}, - }, + Nodes: getNodes(ed.Jaeger.Spec.Storage.Elasticsearch), + }, + } +} + +func getNodes(es v1alpha1.ElasticsearchSpec) []esv1alpha1.ElasticsearchNode { + if es.NodeCount <= 3 { + return []esv1alpha1.ElasticsearchNode{ + { + NodeCount: es.NodeCount, + Storage: es.Storage, + NodeSelector: es.NodeSelector, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleClient, esv1alpha1.ElasticsearchRoleData, esv1alpha1.ElasticsearchRoleMaster}, }, + } + } + return []esv1alpha1.ElasticsearchNode{ + { + NodeCount: 3, + Storage: es.Storage, + NodeSelector: es.NodeSelector, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleMaster}, + }, + { + NodeCount: es.NodeCount - 3, + Storage: es.Storage, + NodeSelector: es.NodeSelector, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleClient, esv1alpha1.ElasticsearchRoleData}, }, } } + +// taken from https://github.com/openshift/cluster-logging-operator/blob/1ead6701c7c7af9c0578aa66597261079b2781d5/vendor/github.com/openshift/elasticsearch-operator/pkg/k8shandler/defaults.go#L33 +func calculateReplicaShards(policyType esv1alpha1.RedundancyPolicyType, dataNodes int) int { + switch policyType { + case esv1alpha1.FullRedundancy: + return dataNodes - 1 + case esv1alpha1.MultipleRedundancy: + return (dataNodes - 1) / 2 + case esv1alpha1.SingleRedundancy: + return 1 + case esv1alpha1.ZeroRedundancy: + return 0 + default: + return 1 + } +} + +func containsPrefix(prefix string, arr []string) bool { + for _, a := range arr { + if strings.HasPrefix(a, prefix) { + return true + } + } + return false +} diff --git a/pkg/storage/elasticsearch_test.go b/pkg/storage/elasticsearch_test.go index d4cd3ab74..9bcf8e52f 100644 --- a/pkg/storage/elasticsearch_test.go +++ b/pkg/storage/elasticsearch_test.go @@ -8,6 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/jaegertracing/jaeger-operator/pkg/apis/io/v1alpha1" + esv1alpha1 "github.com/jaegertracing/jaeger-operator/pkg/storage/elasticsearch/v1alpha1" ) func TestShouldDeployElasticsearch(t *testing.T) { @@ -26,46 +27,152 @@ func TestShouldDeployElasticsearch(t *testing.T) { } func TestCreateElasticsearchCR(t *testing.T) { - trueVar := true - j := v1alpha1.NewJaeger("foo") - j.Namespace = "myproject" - es := &ElasticsearchDeployment{Jaeger: j} - cr := es.createCr() - assert.Equal(t, "myproject", cr.Namespace) - assert.Equal(t, "elasticsearch", cr.Name) - assert.Equal(t, []metav1.OwnerReference{{Name: "foo", Controller: &trueVar}}, cr.OwnerReferences) + tests := []struct { + jEsSpec v1alpha1.ElasticsearchSpec + esSpec esv1alpha1.ElasticsearchSpec + }{ + { + jEsSpec: v1alpha1.ElasticsearchSpec{ + NodeCount: 2, + RedundancyPolicy: esv1alpha1.FullRedundancy, + Storage: esv1alpha1.ElasticsearchStorageSpec{ + StorageClassName: "floppydisk", + }, + }, + esSpec: esv1alpha1.ElasticsearchSpec{ + ManagementState: esv1alpha1.ManagementStateManaged, + RedundancyPolicy: esv1alpha1.FullRedundancy, + Spec: esv1alpha1.ElasticsearchNodeSpec{}, + Nodes: []esv1alpha1.ElasticsearchNode{ + { + NodeCount: 2, + Storage: esv1alpha1.ElasticsearchStorageSpec{StorageClassName: "floppydisk"}, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleClient, esv1alpha1.ElasticsearchRoleData, esv1alpha1.ElasticsearchRoleMaster}, + }, + }, + }, + }, + { + jEsSpec: v1alpha1.ElasticsearchSpec{ + NodeCount: 5, + RedundancyPolicy: esv1alpha1.FullRedundancy, + Storage: esv1alpha1.ElasticsearchStorageSpec{ + StorageClassName: "floppydisk", + }, + }, + esSpec: esv1alpha1.ElasticsearchSpec{ + ManagementState: esv1alpha1.ManagementStateManaged, + RedundancyPolicy: esv1alpha1.FullRedundancy, + Spec: esv1alpha1.ElasticsearchNodeSpec{}, + Nodes: []esv1alpha1.ElasticsearchNode{ + { + NodeCount: 3, + Storage: esv1alpha1.ElasticsearchStorageSpec{StorageClassName: "floppydisk"}, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleMaster}, + }, + { + NodeCount: 2, + Storage: esv1alpha1.ElasticsearchStorageSpec{StorageClassName: "floppydisk"}, + Roles: []esv1alpha1.ElasticsearchNodeRole{esv1alpha1.ElasticsearchRoleClient, esv1alpha1.ElasticsearchRoleData}, + }, + }, + }, + }, + } + for _, test := range tests { + j := v1alpha1.NewJaeger("foo") + j.Namespace = "myproject" + j.Spec.Storage.Elasticsearch = test.jEsSpec + es := &ElasticsearchDeployment{Jaeger: j} + cr := es.createCr() + assert.Equal(t, "myproject", cr.Namespace) + assert.Equal(t, "elasticsearch", cr.Name) + trueVar := true + assert.Equal(t, []metav1.OwnerReference{{Name: "foo", Controller: &trueVar}}, cr.OwnerReferences) + assert.Equal(t, cr.Spec, test.esSpec) + } } func TestInject(t *testing.T) { - p := &v1.PodSpec{ - Containers: []v1.Container{{ - Args: []string{"foo"}, - VolumeMounts: []v1.VolumeMount{{Name: "lol"}}, - }}, - } - es := &ElasticsearchDeployment{Jaeger: v1alpha1.NewJaeger("hoo")} - es.InjectStorageConfiguration(p) - expVolumes := []v1.Volume{{Name: "certs", VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: "hoo-jaeger-elasticsearch", + tests := []struct { + pod *v1.PodSpec + expected *v1.PodSpec + }{ + {pod: &v1.PodSpec{ + Containers: []v1.Container{{ + Args: []string{"foo"}, + VolumeMounts: []v1.VolumeMount{{Name: "lol"}}, + }}, }, - }}} - assert.Equal(t, expVolumes, p.Volumes) - expContainers := []v1.Container{{ - Args: []string{ - "foo", - "--es.server-urls=https://elasticsearch:9200", - "--es.token-file=" + k8sTokenFile, - "--es.tls.ca=" + caPath, + expected: &v1.PodSpec{ + Containers: []v1.Container{{ + Args: []string{ + "foo", + "--es.server-urls=" + elasticsearchUrl, + "--es.token-file=" + k8sTokenFile, + "--es.tls.ca=" + caPath, + "--es.num-shards=0", + "--es.num-replicas=1", + }, + VolumeMounts: []v1.VolumeMount{ + {Name: "lol"}, + {Name: volumeName, ReadOnly: true, MountPath: volumeMountPath}, + }, + }}, + Volumes: []v1.Volume{{Name: "certs", VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "hoo-jaeger-elasticsearch"}}}, + }}, }, - VolumeMounts: []v1.VolumeMount{ - {Name: "lol"}, - { - Name: volumeName, - ReadOnly: true, - MountPath: volumeMountPath, - }, + {pod: &v1.PodSpec{ + Containers: []v1.Container{{ + Args: []string{"--es.num-shards=15"}, + }}, + }, + expected: &v1.PodSpec{ + Containers: []v1.Container{{ + Args: []string{ + "--es.num-shards=15", + "--es.server-urls=" + elasticsearchUrl, + "--es.token-file=" + k8sTokenFile, + "--es.tls.ca=" + caPath, + "--es.num-replicas=1", + }, + VolumeMounts: []v1.VolumeMount{ + {Name: volumeName, ReadOnly: true, MountPath: volumeMountPath}, + }, + }}, + Volumes: []v1.Volume{{Name: "certs", VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "hoo-jaeger-elasticsearch"}}}, + }}, }, - }} - assert.Equal(t, expContainers, p.Containers) + } + + for _, test := range tests { + es := &ElasticsearchDeployment{Jaeger: v1alpha1.NewJaeger("hoo")} + es.InjectStorageConfiguration(test.pod) + assert.Equal(t, test.expected, test.pod) + } + +} + +func TestCalculateReplicaShards(t *testing.T) { + tests := []struct { + dataNodes int + redType esv1alpha1.RedundancyPolicyType + shards int + }{ + {redType: esv1alpha1.ZeroRedundancy, dataNodes: 1, shards: 0}, + {redType: esv1alpha1.ZeroRedundancy, dataNodes: 1, shards: 0}, + {redType: esv1alpha1.SingleRedundancy, dataNodes: 1, shards: 1}, + {redType: esv1alpha1.SingleRedundancy, dataNodes: 20, shards: 1}, + {redType: esv1alpha1.MultipleRedundancy, dataNodes: 1, shards: 0}, + {redType: esv1alpha1.MultipleRedundancy, dataNodes: 20, shards: 9}, + {redType: esv1alpha1.FullRedundancy, dataNodes: 1, shards: 0}, + {redType: esv1alpha1.FullRedundancy, dataNodes: 20, shards: 19}, + } + for _, test := range tests { + assert.Equal(t, test.shards, calculateReplicaShards(test.redType, test.dataNodes)) + } }