Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ECK IPv6 compatible #3654

Merged
merged 20 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
controllerscheme "github.com/elastic/cloud-on-k8s/pkg/controller/common/scheme"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/tracing"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/settings"
"github.com/elastic/cloud-on-k8s/pkg/controller/enterprisesearch"
"github.com/elastic/cloud-on-k8s/pkg/controller/kibana"
"github.com/elastic/cloud-on-k8s/pkg/controller/license"
Expand Down Expand Up @@ -440,6 +441,7 @@ func startOperator(stopChan <-chan struct{}) error {
}
params := operator.Parameters{
Dialer: dialer,
IPFamily: net.ToIPFamily(os.Getenv(settings.EnvPodIP)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should include that as an operator config setting, that defaults to autodetect if empty.
eg. --ip-family=ipv4|ipv6|""
So users can override it if for some reasons the IP family of the operator is not the ip family they want to use for the managed workload.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this would be a future addition. But I think you are right it would be good to have an escape hatch right from the start. Will add it.

OperatorNamespace: operatorNamespace,
OperatorInfo: operatorInfo,
CACertRotation: certificates.RotationParams{
Expand Down
4 changes: 4 additions & 0 deletions config/e2e/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ spec:
- name: ELASTIC_APM_ENVIRONMENT
value: {{ .Pipeline }}-{{ .BuildNumber }}-{{ .Provider }}-{{ .ClusterName }}-{{ .KubernetesVersion }}-{{ .ElasticStackVersion }}
{{end}}
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
resources:
limits:
cpu: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
{{- if .Values.config.webhook.enabled }}
- name: WEBHOOK_SECRET
value: "{{ .Values.config.webhook.certsSecret }}"
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/elasticsearch/v1/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func validSanIP(es *Elasticsearch) field.ErrorList {
if selfSignedCerts != nil {
for _, san := range selfSignedCerts.SubjectAlternativeNames {
if san.IP != "" {
ip := netutil.MaybeIPTo4(net.ParseIP(san.IP))
ip := netutil.IPToRFCForm(net.ParseIP(san.IP))
if ip == nil {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("http", "tls", "selfSignedCertificate", "subjectAlternativeNames"), san.IP, invalidSanIPErrMsg))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/apis/elasticsearch/v1beta1/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func validSanIP(es *Elasticsearch) field.ErrorList {
if selfSignedCerts != nil {
for _, san := range selfSignedCerts.SubjectAlternativeNames {
if san.IP != "" {
ip := netutil.MaybeIPTo4(net.ParseIP(san.IP))
ip := netutil.IPToRFCForm(net.ParseIP(san.IP))
if ip == nil {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("http", "tls", "selfSignedCertificate", "subjectAlternativeNames"), san.IP, invalidSanIPErrMsg))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/common/certificates/http_reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ func createValidatedHTTPCertificateTemplate(
dnsNames = append(dnsNames, san.DNS)
}
if san.IP != "" {
ipAddresses = append(ipAddresses, netutil.MaybeIPTo4(net.ParseIP(san.IP)))
ipAddresses = append(ipAddresses, netutil.IPToRFCForm(net.ParseIP(san.IP)))
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/controller/common/operator/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package operator

import (
"go.elastic.co/apm"
corev1 "k8s.io/api/core/v1"

"github.com/elastic/cloud-on-k8s/pkg/about"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates"
Expand All @@ -20,6 +21,8 @@ type Parameters struct {
OperatorInfo about.OperatorInfo
// Dialer is used to create the Elasticsearch HTTP client.
Dialer net.Dialer
// IPFamily represents the IP family to use when creating configuration and services.
IPFamily corev1.IPFamily
// CACertRotation defines the rotation params for CA certificates.
CACertRotation certificates.RotationParams
// CertRotation defines the rotation params for non-CA certificates.
Expand Down
10 changes: 7 additions & 3 deletions pkg/controller/elasticsearch/certificates/transport/csr.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"net"
"time"

"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/sset"

netutil "github.com/elastic/cloud-on-k8s/pkg/utils/net"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"

esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/certificates"
netutil "github.com/elastic/cloud-on-k8s/pkg/utils/net"
)

// createValidatedCertificateTemplate validates a CSR and creates a certificate template.
Expand Down Expand Up @@ -90,8 +92,10 @@ func buildGeneralNames(
// add the transport service name for remote cluster connections initially connecting through the service
// the DNS name has to match the seed hosts configured in the remote cluster settings
{DNSName: fmt.Sprintf("%s.%s.svc", esv1.TransportService(cluster.Name), cluster.Namespace)},
{IPAddress: netutil.MaybeIPTo4(podIP)},
{IPAddress: net.ParseIP("127.0.0.1").To4()},
// add the resolvable DNS name of the Pod as published by Elasticsearch
{DNSName: sset.PodDNSName(pod)},
{IPAddress: netutil.IPToRFCForm(podIP)},
{IPAddress: netutil.IPToRFCForm(netutil.LoopbackFor(netutil.ToIPFamily(podIP.String())))},
}

return generalNames, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func Test_buildGeneralNames(t *testing.T) {
{OtherName: *otherName},
{DNSName: expectedCommonName},
{DNSName: expectedTransportSvcName},
{DNSName: "test-pod-name.test-sset"},
{IPAddress: net.ParseIP(testIP).To4()},
{IPAddress: net.ParseIP("127.0.0.1").To4()},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"crypto/x509/pkix"
"encoding/pem"

"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/label"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand All @@ -34,6 +35,9 @@ var (
testPod = corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod-name",
Labels: map[string]string{
label.StatefulSetNameLabelName: "test-sset",
},
},
Status: corev1.PodStatus{
PodIP: testIP,
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/elasticsearch/driver/upscale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ func TestHandleUpscaleAndSpecChanges(t *testing.T) {
require.Equal(t, pointer.Int32(4), sset2.Spec.Replicas)
comparison.RequireEqual(t, &updatedStatefulSets[1], &sset2)
// headless services should be created for both
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: nodespec.HeadlessServiceName("sset1")}, &corev1.Service{}))
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: nodespec.HeadlessServiceName("sset2")}, &corev1.Service{}))
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: sset.HeadlessServiceName("sset1")}, &corev1.Service{}))
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: sset.HeadlessServiceName("sset2")}, &corev1.Service{}))
// config should be created for both
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: esv1.ConfigSecret("sset1")}, &corev1.Secret{}))
require.NoError(t, k8sClient.Get(types.NamespacedName{Namespace: "ns", Name: esv1.ConfigSecret("sset2")}, &corev1.Secret{}))
Expand Down
4 changes: 3 additions & 1 deletion pkg/controller/elasticsearch/nodespec/podspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"crypto/sha256"
"fmt"

"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/sset"

corev1 "k8s.io/api/core/v1"

esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1"
Expand Down Expand Up @@ -58,7 +60,7 @@ func BuildPodTemplateSpec(
WithPorts(defaultContainerPorts).
WithReadinessProbe(*NewReadinessProbe()).
WithAffinity(DefaultAffinity(es.Name)).
WithEnv(DefaultEnvVars(es.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(es.Name, nodeSet.Name)))...).
WithEnv(DefaultEnvVars(es.Spec.HTTP, sset.HeadlessServiceName(esv1.StatefulSet(es.Name, nodeSet.Name)))...).
WithVolumes(volumes...).
WithVolumeMounts(volumeMounts...).
WithInitContainers(initContainers...).
Expand Down
6 changes: 4 additions & 2 deletions pkg/controller/elasticsearch/nodespec/podspec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"sort"
"testing"

"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/sset"

commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1"
esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/defaults"
Expand Down Expand Up @@ -137,7 +139,7 @@ func TestBuildPodTemplateSpec(t *testing.T) {
Labels: map[string]string{
"common.k8s.elastic.co/type": "elasticsearch",
"elasticsearch.k8s.elastic.co/cluster-name": "name",
"elasticsearch.k8s.elastic.co/config-hash": "2311754148",
"elasticsearch.k8s.elastic.co/config-hash": "3785137207",
"elasticsearch.k8s.elastic.co/http-scheme": "https",
"elasticsearch.k8s.elastic.co/node-data": "false",
"elasticsearch.k8s.elastic.co/node-ingest": "true",
Expand Down Expand Up @@ -173,7 +175,7 @@ func TestBuildPodTemplateSpec(t *testing.T) {
},
Env: append(
[]corev1.EnvVar{{Name: "my-env", Value: "my-value"}},
DefaultEnvVars(sampleES.Spec.HTTP, HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name)))...),
DefaultEnvVars(sampleES.Spec.HTTP, sset.HeadlessServiceName(esv1.StatefulSet(sampleES.Name, nodeSet.Name)))...),
Resources: DefaultResources,
VolumeMounts: volumeMounts,
ReadinessProbe: NewReadinessProbe(),
Expand Down
12 changes: 10 additions & 2 deletions pkg/controller/elasticsearch/nodespec/readiness_probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,17 @@ else
BASIC_AUTH=''
fi

# Check if we are using IPv6
if [[ $POD_IP =~ .*:.* ]]; then
LOOPBACK=[::1]
pebrc marked this conversation as resolved.
Show resolved Hide resolved
else
LOOPBACK=127.0.01
fi

# request Elasticsearch on /
ENDPOINT="${READINESS_PROBE_PROTOCOL:-https}://127.0.0.1:9200/"
status=$(curl -o /dev/null -w "%{http_code}" --max-time ${READINESS_PROBE_TIMEOUT} -XGET -s -k ${BASIC_AUTH} $ENDPOINT)
# we are turning globbing off to allow for unescaped [] in case of IPv6
ENDPOINT="${READINESS_PROBE_PROTOCOL:-https}://${LOOPBACK}:9200/"
status=$(curl -o /dev/null -w "%{http_code}" --max-time ${READINESS_PROBE_TIMEOUT} -XGET -g -s -k ${BASIC_AUTH} $ENDPOINT)
curl_rc=$?

if [[ ${curl_rc} -ne 0 ]]; then
Expand Down
12 changes: 4 additions & 8 deletions pkg/controller/elasticsearch/nodespec/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,14 @@ var (
f = false
)

// HeadlessServiceName returns the name of the headless service for the given StatefulSet.
func HeadlessServiceName(ssetName string) string {
// just use the sset name
return ssetName
}

// HeadlessService returns a headless service for the given StatefulSet
func HeadlessService(es *esv1.Elasticsearch, ssetName string) corev1.Service {
nsn := k8s.ExtractNamespacedName(es)

return corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: nsn.Namespace,
Name: HeadlessServiceName(ssetName),
Name: sset.HeadlessServiceName(ssetName),
Labels: label.NewStatefulSetLabels(nsn, ssetName),
},
Spec: corev1.ServiceSpec{
Expand All @@ -54,6 +48,8 @@ func HeadlessService(es *esv1.Elasticsearch, ssetName string) corev1.Service {
Port: network.HTTPPort,
},
},
// allow nodes to discover themselves via DNS while they are booting up ie. are not ready yet
PublishNotReadyAddresses: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we will want to flag this in the release notes since it could cause issues if people were relying on the old behavior

},
}
}
Expand Down Expand Up @@ -113,7 +109,7 @@ func BuildStatefulSet(
// use default revision history limit
RevisionHistoryLimit: nil,
// build a headless service per StatefulSet, matching the StatefulSet labels
ServiceName: HeadlessServiceName(statefulSetName),
ServiceName: sset.HeadlessServiceName(statefulSetName),
Selector: &metav1.LabelSelector{
MatchLabels: ssetSelector,
},
Expand Down
5 changes: 2 additions & 3 deletions pkg/controller/elasticsearch/settings/masters.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package settings

import (
"context"
"fmt"
"reflect"
"sort"
"strings"
Expand All @@ -16,7 +15,7 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/controller/common/reconciler"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/tracing"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/label"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/network"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/sset"
"github.com/elastic/cloud-on-k8s/pkg/controller/elasticsearch/volume"
"github.com/elastic/cloud-on-k8s/pkg/utils/k8s"
"go.elastic.co/apm"
Expand Down Expand Up @@ -58,7 +57,7 @@ func UpdateSeedHostsConfigMap(
if len(master.Status.PodIP) > 0 { // do not add pod with no IPs
seedHosts = append(
seedHosts,
fmt.Sprintf("%s:%d", master.Status.PodIP, network.TransportPort),
sset.PodDNSName(master),
)
}
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/controller/elasticsearch/settings/masters_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func newPodWithIP(name, ip string, master bool) corev1.Pod {
},
}
label.NodeTypesMasterLabelName.Set(master, p.Labels)
p.Labels[label.StatefulSetNameLabelName] = "test-sset"
return p
}

Expand Down Expand Up @@ -97,7 +98,7 @@ func TestUpdateSeedHostsConfigMap(t *testing.T) {
es: es,
},
wantErr: false,
expectedContent: "10.0.3.3:9300\n10.0.9.2:9300",
expectedContent: "master1.test-sset\nmaster3.test-sset",
},
{
name: "All masters have IPs, some nodes don't",
Expand All @@ -113,7 +114,7 @@ func TestUpdateSeedHostsConfigMap(t *testing.T) {
es: es,
},
wantErr: false,
expectedContent: "10.0.3.3:9300\n10.0.6.5:9300\n10.0.9.2:9300",
expectedContent: "master1.test-sset\nmaster2.test-sset\nmaster3.test-sset",
},
{
name: "Ordering of pods should not matter",
Expand All @@ -127,7 +128,7 @@ func TestUpdateSeedHostsConfigMap(t *testing.T) {
es: es,
},
wantErr: false,
expectedContent: "10.0.3.3:9300\n10.0.6.5:9300\n10.0.9.2:9300",
expectedContent: "master1.test-sset\nmaster2.test-sset\nmaster3.test-sset",
},
}
for _, tt := range tests {
Expand Down
6 changes: 3 additions & 3 deletions pkg/controller/elasticsearch/settings/merged_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ func baseConfig(clusterName string, ver version.Version) *CanonicalConfig {
esv1.NodeName: "${" + EnvPodName + "}",
esv1.ClusterName: clusterName,

// derive IP dynamically from the pod IP, injected as env var
esv1.NetworkPublishHost: "${" + EnvPodIP + "}",
esv1.NetworkHost: "0.0.0.0",
// use the DNS name as the publish host
esv1.NetworkPublishHost: fmt.Sprintf("${%s}.${%s}", EnvPodName, HeadlessServiceName),
esv1.NetworkHost: "0",

// allow ES to be aware of k8s node the pod is running on when allocating shards
esv1.ShardAwarenessAttributes: nodeAttrK8sNodeName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func TestNewMergedESConfig(t *testing.T) {
cfgBytes, err := cfg.Render()
require.NoError(t, err)
// default config is still there
require.True(t, bytes.Contains(cfgBytes, []byte("publish_host: ${POD_IP}")))
require.True(t, bytes.Contains(cfgBytes, []byte("publish_host: ${POD_NAME}.${HEADLESS_SERVICE_NAME}")))
// but has been overridden
require.True(t, bytes.Contains(cfgBytes, []byte("seed_providers: something-else")))
require.Equal(t, 1, bytes.Count(cfgBytes, []byte("seed_providers:")))
Expand Down
12 changes: 12 additions & 0 deletions pkg/controller/elasticsearch/sset/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/utils/stringsutil"
)

// HeadlessServiceName returns the name of the headless service for the given StatefulSet.
func HeadlessServiceName(ssetName string) string {
// just use the sset name
return ssetName
}

//PodDNSName returns the DNS resolvable name of the given pod resolvable within the namespace.
func PodDNSName(pod corev1.Pod) string {
ssetName := pod.Labels[label.StatefulSetNameLabelName]
return fmt.Sprintf("%s.%s", pod.Name, HeadlessServiceName(ssetName))
}

// PodName returns the name of the pod with the given ordinal for this StatefulSet.
func PodName(ssetName string, ordinal int32) string {
return fmt.Sprintf("%s-%d", ssetName, ordinal)
Expand Down
Loading