Skip to content

Commit

Permalink
Add metadata for transform node role (elastic#3361)
Browse files Browse the repository at this point in the history
Elasticsearch 7.7.0 introduces the transform node role. Add this new node type to the pod metadata when running a version of Elasticsearch that supports transform. Adjust test cases and default values.
  • Loading branch information
pebrc authored Jul 1, 2020
1 parent d1a763e commit 284e16d
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 48 deletions.
45 changes: 28 additions & 17 deletions pkg/apis/elasticsearch/v1/elasticsearch_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
package v1

import (
"github.com/elastic/cloud-on-k8s/pkg/controller/common/version"
"github.com/elastic/go-ucfg"

commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1"
)

const (
NodeData = "node.data"
NodeIngest = "node.ingest"
NodeMaster = "node.master"
NodeML = "node.ml"
NodeData = "node.data"
NodeIngest = "node.ingest"
NodeMaster = "node.master"
NodeML = "node.ml"
NodeTransform = "node.transform"
)

// ClusterSettings is the cluster node in elasticsearch.yml.
Expand All @@ -24,10 +26,11 @@ type ClusterSettings struct {

// Node is the node section in elasticsearch.yml.
type Node struct {
Master bool `config:"master"`
Data bool `config:"data"`
Ingest bool `config:"ingest"`
ML bool `config:"ml"`
Master bool `config:"master"`
Data bool `config:"data"`
Ingest bool `config:"ingest"`
ML bool `config:"ml"`
Transform bool `config:"transform"` // available as of 7.7.0
}

// ElasticsearchSettings is a typed subset of elasticsearch.yml for purposes of the operator.
Expand All @@ -37,18 +40,26 @@ type ElasticsearchSettings struct {
}

// DefaultCfg is an instance of ElasticsearchSettings with defaults set as they are in Elasticsearch.
var DefaultCfg = ElasticsearchSettings{
Node: Node{
Master: true,
Data: true,
Ingest: true,
ML: true,
},
func DefaultCfg(ver version.Version) ElasticsearchSettings {
settings := ElasticsearchSettings{
Node: Node{
Master: true,
Data: true,
Ingest: true,
ML: true,
Transform: true,
},
}
if !ver.IsSameOrAfter(version.From(7, 7, 0)) {
// this setting did not exist before 7.7.0 expressed here by setting it to false this allows us to keep working with just one model
settings.Node.Transform = false
}
return settings
}

// Unpack unpacks Config into a typed subset.
func UnpackConfig(c *commonv1.Config) (ElasticsearchSettings, error) {
esSettings := DefaultCfg // defensive copy
func UnpackConfig(c *commonv1.Config, ver version.Version) (ElasticsearchSettings, error) {
esSettings := DefaultCfg(ver)
if c == nil {
// make this nil safe to allow a ptr value to work around Json serialization issues
return esSettings, nil
Expand Down
46 changes: 37 additions & 9 deletions pkg/apis/elasticsearch/v1/elasticsearch_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import (
"testing"

commonv1 "github.com/elastic/cloud-on-k8s/pkg/apis/common/v1"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/version"
"github.com/go-test/deep"
"github.com/stretchr/testify/require"
)

func TestConfig_RoleDefaults(t *testing.T) {
type args struct {
c2 commonv1.Config
c2 commonv1.Config
ver version.Version
}
tests := []struct {
name string
Expand Down Expand Up @@ -61,12 +63,36 @@ func TestConfig_RoleDefaults(t *testing.T) {
},
want: false,
},
{
name: "version specific default differences 1",
c: commonv1.Config{
Data: map[string]interface{}{
NodeTransform: true,
},
},
args: args{
ver: version.From(7, 5, 0),
},
want: false,
},
{
name: "version specific default differences 2",
c: commonv1.Config{
Data: map[string]interface{}{
NodeTransform: true,
},
},
args: args{
ver: version.From(7, 7, 0),
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c1, err := UnpackConfig(&tt.c)
c1, err := UnpackConfig(&tt.c, tt.args.ver)
require.NoError(t, err)
c2, err := UnpackConfig(&tt.args.c2)
c2, err := UnpackConfig(&tt.args.c2, tt.args.ver)
require.NoError(t, err)
if got := c1.Node == c2.Node; got != tt.want {
t.Errorf("Config.EqualRoles() = %v, want %v", got, tt.want)
Expand Down Expand Up @@ -148,6 +174,7 @@ func TestConfig_DeepCopy(t *testing.T) {
}

func TestConfig_Unpack(t *testing.T) {
ver := version.From(7, 7, 0)
tests := []struct {
name string
args *commonv1.Config
Expand All @@ -169,10 +196,11 @@ func TestConfig_Unpack(t *testing.T) {
},
want: ElasticsearchSettings{
Node: Node{
Master: false,
Data: true,
Ingest: true,
ML: true,
Master: false,
Data: true,
Ingest: true,
ML: true,
Transform: true,
},
Cluster: ClusterSettings{
InitialMasterNodes: []string{"a", "b"},
Expand All @@ -183,13 +211,13 @@ func TestConfig_Unpack(t *testing.T) {
{
name: "Unpack is nil safe",
args: nil,
want: DefaultCfg,
want: DefaultCfg(ver),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnpackConfig(tt.args)
got, err := UnpackConfig(tt.args, ver)
if (err != nil) != tt.wantErr {
t.Errorf("Config.Unpack() error = %v, wantErr %v", err, tt.wantErr)
return
Expand Down
6 changes: 5 additions & 1 deletion pkg/apis/elasticsearch/v1/validations.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ func supportedVersion(es *Elasticsearch) field.ErrorList {
func hasMaster(es *Elasticsearch) field.ErrorList {
var errs field.ErrorList
var hasMaster bool
v, err := version.Parse(es.Spec.Version)
if err != nil {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("version"), es.Spec.Version, parseVersionErrMsg))
}
for i, t := range es.Spec.NodeSets {
cfg, err := UnpackConfig(t.Config)
cfg, err := UnpackConfig(t.Config, *v)
if err != nil {
errs = append(errs, field.Invalid(field.NewPath("spec").Child("nodeSets").Index(i), t.Config, cfgInvalidMsg))
}
Expand Down
14 changes: 10 additions & 4 deletions pkg/controller/elasticsearch/label/label.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const (
NodeTypesIngestLabelName common.TrueFalseLabel = "elasticsearch.k8s.elastic.co/node-ingest"
// NodeTypesMLLabelName is a label set to true on nodes with the ml role
NodeTypesMLLabelName common.TrueFalseLabel = "elasticsearch.k8s.elastic.co/node-ml"
// NodeTypesTransformLabelName is a label set to true on nodes with the transform role
NodeTypesTransformLabelName common.TrueFalseLabel = "elasticsearch.k8s.elastic.co/node-transform"

HTTPSchemeLabelName = "elasticsearch.k8s.elastic.co/http-scheme"

Expand Down Expand Up @@ -112,21 +114,25 @@ func NewLabels(es types.NamespacedName) map[string]string {
func NewPodLabels(
es types.NamespacedName,
ssetName string,
version version.Version,
ver version.Version,
nodeRoles esv1.Node,
configHash string,
scheme string,
) (map[string]string, error) {
) map[string]string {
// cluster name based labels
labels := NewLabels(es)
// version label
labels[VersionLabelName] = version.String()
labels[VersionLabelName] = ver.String()

// node types labels
NodeTypesMasterLabelName.Set(nodeRoles.Master, labels)
NodeTypesDataLabelName.Set(nodeRoles.Data, labels)
NodeTypesIngestLabelName.Set(nodeRoles.Ingest, labels)
NodeTypesMLLabelName.Set(nodeRoles.ML, labels)
// transform nodes were only added in 7.7.0 so we should not annotate previous versions with them
if ver.IsSameOrAfter(version.From(7, 7, 0)) {
NodeTypesTransformLabelName.Set(nodeRoles.Transform, labels)
}

// config hash label, to rotate pods on config changes
labels[ConfigHashLabelName] = configHash
Expand All @@ -138,7 +144,7 @@ func NewPodLabels(
labels[k] = v
}

return labels, nil
return labels
}

// NewConfigLabels returns labels to apply for an Elasticsearch Config secret.
Expand Down
92 changes: 92 additions & 0 deletions pkg/controller/elasticsearch/label/label_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import (
"reflect"
"testing"

v1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1"
"github.com/elastic/cloud-on-k8s/pkg/controller/common"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/version"
"github.com/go-test/deep"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
Expand Down Expand Up @@ -168,3 +171,92 @@ func TestMinVersion(t *testing.T) {
})
}
}

func TestNewPodLabels(t *testing.T) {
type args struct {
es types.NamespacedName
ssetName string
ver version.Version
nodeRoles v1.Node
configHash string
scheme string
}
nameFixture := types.NamespacedName{
Namespace: "ns",
Name: "name",
}
tests := []struct {
name string
args args
want map[string]string
wantErr bool
}{
{
name: "labels pre-7.7",
args: args{
es: nameFixture,
ssetName: "sset",
ver: version.From(7, 1, 0),
nodeRoles: v1.Node{
Master: false,
Data: false,
Ingest: false,
ML: false,
Transform: false,
},
configHash: "hash",
scheme: "https",
},
want: map[string]string{
ClusterNameLabelName: "name",
common.TypeLabelName: "elasticsearch",
VersionLabelName: "7.1.0",
string(NodeTypesMasterLabelName): "false",
string(NodeTypesDataLabelName): "false",
string(NodeTypesIngestLabelName): "false",
string(NodeTypesMLLabelName): "false",
ConfigHashLabelName: "hash",
HTTPSchemeLabelName: "https",
StatefulSetNameLabelName: "sset",
},
wantErr: false,
},
{
name: "labels post-7.7",
args: args{
es: nameFixture,
ssetName: "sset",
ver: version.From(7, 7, 0),
nodeRoles: v1.Node{
Master: false,
Data: true,
Ingest: false,
ML: false,
Transform: true,
},
configHash: "hash",
scheme: "https",
},
want: map[string]string{
ClusterNameLabelName: "name",
common.TypeLabelName: "elasticsearch",
VersionLabelName: "7.7.0",
string(NodeTypesMasterLabelName): "false",
string(NodeTypesDataLabelName): "true",
string(NodeTypesIngestLabelName): "false",
string(NodeTypesMLLabelName): "false",
string(NodeTypesTransformLabelName): "true",
ConfigHashLabelName: "hash",
HTTPSchemeLabelName: "https",
StatefulSetNameLabelName: "sset",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewPodLabels(tt.args.es, tt.args.ssetName, tt.args.ver, tt.args.nodeRoles, tt.args.configHash, tt.args.scheme)
require.Nil(t, deep.Equal(got, tt.want))
})
}
}
17 changes: 7 additions & 10 deletions pkg/controller/elasticsearch/nodespec/podspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,28 +90,25 @@ func buildLabels(
nodeSet esv1.NodeSet,
keystoreResources *keystore.Resources,
) (map[string]string, error) {
// label with a hash of the config to rotate the pod on config changes
unpackedCfg, err := cfg.Unpack()
// label with version
ver, err := version.Parse(es.Spec.Version)
if err != nil {
return nil, err
}
nodeRoles := unpackedCfg.Node
cfgHash := hash.HashObject(cfg)

// label with version
ver, err := version.Parse(es.Spec.Version)
// label with a hash of the config to rotate the pod on config changes
unpackedCfg, err := cfg.Unpack(*ver)
if err != nil {
return nil, err
}
nodeRoles := unpackedCfg.Node
cfgHash := hash.HashObject(cfg)

podLabels, err := label.NewPodLabels(
podLabels := label.NewPodLabels(
k8s.ExtractNamespacedName(&es),
esv1.StatefulSet(es.Name, nodeSet.Name),
*ver, nodeRoles, cfgHash, es.Spec.HTTP.Protocol(),
)
if err != nil {
return nil, err
}

if keystoreResources != nil {
// label with a checksum of the secure settings to rotate the pod on secure settings change
Expand Down
5 changes: 3 additions & 2 deletions pkg/controller/elasticsearch/settings/canonical_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package settings
import (
esv1 "github.com/elastic/cloud-on-k8s/pkg/apis/elasticsearch/v1"
common "github.com/elastic/cloud-on-k8s/pkg/controller/common/settings"
"github.com/elastic/cloud-on-k8s/pkg/controller/common/version"
)

// CanonicalConfig contains configuration for Elasticsearch ("elasticsearch.yml"),
Expand All @@ -20,8 +21,8 @@ func NewCanonicalConfig() CanonicalConfig {
}

// Unpack returns a typed subset of Elasticsearch settings.
func (c CanonicalConfig) Unpack() (esv1.ElasticsearchSettings, error) {
cfg := esv1.DefaultCfg
func (c CanonicalConfig) Unpack(ver version.Version) (esv1.ElasticsearchSettings, error) {
cfg := esv1.DefaultCfg(ver)
err := c.CanonicalConfig.Unpack(&cfg)
return cfg, err
}
Loading

0 comments on commit 284e16d

Please sign in to comment.