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

Report total memory under management for licensing #2277

Merged
merged 34 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
61399ca
Report total memory under management for licensing
thbkrkr Dec 16, 2019
562d835
Apply review's input
thbkrkr Dec 17, 2019
d7770cc
Update and document CurrentEnterpriseLicense
thbkrkr Dec 17, 2019
5449fa1
Factorize code with containerMemLimits function
thbkrkr Dec 17, 2019
4c1febf
Improve maxHeapSizePattern
thbkrkr Dec 17, 2019
287f0bc
Enterprise trial license are valid
thbkrkr Dec 17, 2019
6aec330
Seb's feedbacks
thbkrkr Dec 17, 2019
9001680
Anurag's feedbacks
thbkrkr Dec 17, 2019
2054563
Package renaming
thbkrkr Dec 17, 2019
f8b86bf
Unit test LicenseInfo#toMap
thbkrkr Dec 17, 2019
37f9bd9
More comments
thbkrkr Dec 18, 2019
82f331e
Improve maxHeapSize regexp
thbkrkr Dec 18, 2019
9372606
Use math.Ceil
thbkrkr Dec 18, 2019
e1d59d5
Unit test CurrentEnterpriseLicense
thbkrkr Dec 18, 2019
33c9379
Fix memory accounting
thbkrkr Dec 18, 2019
3126a33
Unit test ResourceReporter.Get()
thbkrkr Dec 18, 2019
5ed6e47
Merge remote-tracking branch 'upstream/master' into memory-accounting…
thbkrkr Dec 18, 2019
ffbeeb2
Update aggregator unit tests
thbkrkr Dec 18, 2019
4cde6b4
Wait for the cache sync before starting the resource reporter
thbkrkr Dec 18, 2019
cfc65fd
Improve maxHeapSizePattern
thbkrkr Dec 18, 2019
dbac4ee
Use errors.Wrap in aggregate functions
thbkrkr Dec 18, 2019
073ed06
Use a non-capturing group
thbkrkr Dec 18, 2019
1f5f1c3
Mark license info config map with a label
thbkrkr Dec 19, 2019
b923642
Test ResourceReporter#Start()
thbkrkr Dec 19, 2019
e1ec406
Add namespace to info logs
thbkrkr Dec 19, 2019
bc379a9
Update comments
thbkrkr Dec 19, 2019
4bd051e
Accept -Xmx without unit
thbkrkr Dec 19, 2019
4c5b4f4
cmd/licensing-info/main.go doc
thbkrkr Dec 19, 2019
838e905
Revisit how to report as soon as possible
thbkrkr Dec 20, 2019
7548f79
A little simpler approach
thbkrkr Dec 20, 2019
7de0abd
Remove excess multiplication
thbkrkr Dec 20, 2019
1228b3c
Increase waitFor duration in Test_Start
thbkrkr Dec 20, 2019
c6a68fe
Removing jq from the usage example
thbkrkr Dec 20, 2019
eb52541
Move ResourceReporterFrequency in the licence package
thbkrkr Dec 20, 2019
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
51 changes: 51 additions & 0 deletions cmd/licensing-info/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package main

import (
"encoding/json"
"fmt"
"log"

eckscheme "github.com/elastic/cloud-on-k8s/pkg/controller/common/scheme"
"github.com/elastic/cloud-on-k8s/pkg/resource"
"k8s.io/client-go/kubernetes/scheme"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // auth on gke
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)

func main() {
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
licensingInfo, err := resource.NewLicensingReporter(newK8sClient()).Get()
if err != nil {
log.Fatal(err, "Failed to get licensing info")
}

bytes, err := json.Marshal(licensingInfo)
if err != nil {
log.Fatal(err, "Failed to marshal licensing info")
}

fmt.Print(string(bytes))
}

func newK8sClient() client.Client {
cfg, err := config.GetConfig()
if err != nil {
log.Fatal(err, "Failed to get a Kubernetes config")
}

err = eckscheme.SetupScheme()
if err != nil {
log.Fatal(err, "Failed to set up the ECK scheme")
}

c, err := client.New(cfg, client.Options{Scheme: scheme.Scheme})
if err != nil {
log.Fatal(err, "Failed to create a new Kubernetes client")
}

return c
}
7 changes: 5 additions & 2 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ import (
"strings"
"time"

"k8s.io/client-go/rest"

// allow gcp authentication
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/client-go/kubernetes"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
logf "sigs.k8s.io/controller-runtime/pkg/log"
Expand All @@ -45,6 +44,7 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/controller/webhook"
"github.com/elastic/cloud-on-k8s/pkg/dev"
"github.com/elastic/cloud-on-k8s/pkg/dev/portforward"
"github.com/elastic/cloud-on-k8s/pkg/resource"
"github.com/elastic/cloud-on-k8s/pkg/utils/net"
)

Expand Down Expand Up @@ -321,6 +321,9 @@ func execute() {
log.Error(err, "unable to create controller", "controller", "LicenseTrial")
os.Exit(1)
}

r := resource.NewLicensingReporter(mgr.GetClient())
go r.Start(operatorNamespace)
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
}

log.Info("Starting the manager", "uuid", operatorInfo.OperatorUUID,
Expand Down
19 changes: 11 additions & 8 deletions pkg/controller/apmserver/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ const (
ConfigVolumePath = ApmBaseDir + "/config"
)

var DefaultResources = corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("512Mi"),
},
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("512Mi"),
},
}
var (
DefaultMemoryLimits = resource.MustParse("512Mi")
DefaultResources = corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: DefaultMemoryLimits,
},
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: DefaultMemoryLimits,
},
}
)

func readinessProbe(tls bool) corev1.Probe {
scheme := corev1.URISchemeHTTP
Expand Down
37 changes: 31 additions & 6 deletions pkg/controller/common/license/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package license

import (
"sort"
"time"

"github.com/elastic/cloud-on-k8s/pkg/utils/k8s"
Expand All @@ -14,6 +15,7 @@ import (
)

type Checker interface {
CurrentEnterpriseLicense() (*EnterpriseLicense, error)
EnterpriseFeaturesEnabled() (bool, error)
Valid(l EnterpriseLicense) (bool, error)
}
Expand Down Expand Up @@ -45,23 +47,42 @@ func (lc *checker) publicKeyFor(l EnterpriseLicense) ([]byte, error) {
}, &signatureSec)
}

// EnterpriseFeaturesEnabled returns true if a valid enterprise license is installed.
func (lc *checker) EnterpriseFeaturesEnabled() (bool, error) {
// CurrentEnterpriseLicense returns the currently valid Enterprise license if installed.
func (lc *checker) CurrentEnterpriseLicense() (*EnterpriseLicense, error) {
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
licenses, err := EnterpriseLicenses(lc.k8sClient)
if err != nil {
return false, errors.Wrap(err, "failed to list enterprise licenses")
return nil, errors.Wrap(err, "failed to list enterprise licenses")
}

sort.Slice(licenses, func(i, j int) bool {
t1, t2 := EnterpriseLicenseTypeOrder[licenses[i].License.Type], EnterpriseLicenseTypeOrder[licenses[j].License.Type]
if t1 != t2 { // sort by type (first the most features)
return t1 > t2
}
// and by expiry date (first which expires in a long time)
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
return licenses[i].License.ExpiryDateInMillis > licenses[j].License.ExpiryDateInMillis
})

// pick the first valid Enterprise license in the sorted slice
for _, l := range licenses {
valid, err := lc.Valid(l)
if err != nil {
return false, err
return nil, err
}
if valid {
return true, nil
return &l, nil
}
}
return false, nil
return nil, nil
}

// EnterpriseFeaturesEnabled returns true if a valid enterprise license is installed.
func (lc *checker) EnterpriseFeaturesEnabled() (bool, error) {
license, err := lc.CurrentEnterpriseLicense()
if err != nil {
return false, err
}
return license != nil, nil
}

// Valid returns true if the given Enterprise license is valid or an error if any.
Expand All @@ -83,6 +104,10 @@ func (lc *checker) Valid(l EnterpriseLicense) (bool, error) {

type MockChecker struct{}

func (MockChecker) CurrentEnterpriseLicense() (*EnterpriseLicense, error) {
return &EnterpriseLicense{}, nil
}

func (MockChecker) EnterpriseFeaturesEnabled() (bool, error) {
return true, nil
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/controller/common/license/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ type LicenseSpec struct {
ClusterLicenses []ElasticsearchLicense `json:"cluster_licenses"`
}

// EnterpriseLicenseTypeOrder license types mapped to ints in increasing order of feature sets for sorting purposes.
var EnterpriseLicenseTypeOrder = map[OperatorLicenseType]int{
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
LicenseTypeEnterpriseTrial: 0,
LicenseTypeEnterprise: 1,
}

// StartTime is the date as of which this license is valid.
func (l EnterpriseLicense) StartTime() time.Time {
return time.Unix(0, l.License.StartDateInMillis*int64(time.Millisecond))
Expand Down
5 changes: 3 additions & 2 deletions pkg/controller/elasticsearch/nodespec/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@ var (
{Name: "transport", ContainerPort: network.TransportPort, Protocol: corev1.ProtocolTCP},
}

DefaultMemoryLimits = resource.MustParse("2Gi")
// DefaultResources for the Elasticsearch container. The JVM default heap size is 1Gi, so we
// request at least 2Gi for the container to make sure ES can work properly.
// Not applying this minimum default would make ES randomly crash (OOM) on small machines.
// Similarly, we apply a default memory limit of 2Gi, to ensure the Pod isn't the first one to get evicted.
// No CPU requirement is set by default.
DefaultResources = corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("2Gi"),
corev1.ResourceMemory: DefaultMemoryLimits,
},
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("2Gi"),
corev1.ResourceMemory: DefaultMemoryLimits,
},
}
)
Expand Down
6 changes: 5 additions & 1 deletion pkg/controller/kibana/config/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import (
)

// Kibana configuration settings file
const SettingsFilename = "kibana.yml"
const (
SettingsFilename = "kibana.yml"
EnvNodeOpts = "NODE_OPTS"
thbkrkr marked this conversation as resolved.
Show resolved Hide resolved
)


// CanonicalConfig contains configuration for Kibana ("kibana.yml"),
// as a hierarchical key-value configuration.
Expand Down
19 changes: 11 additions & 8 deletions pkg/controller/kibana/pod/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ var ports = []corev1.ContainerPort{
{Name: "http", ContainerPort: int32(HTTPPort), Protocol: corev1.ProtocolTCP},
}

var DefaultResources = corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: resource.MustParse("1Gi"),
},
}
var (
DefaultMemoryLimits = resource.MustParse("1Gi")
DefaultResources = corev1.ResourceRequirements{
Requests: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: DefaultMemoryLimits,
},
Limits: map[corev1.ResourceName]resource.Quantity{
corev1.ResourceMemory: DefaultMemoryLimits,
},
}
)

// readinessProbe is the readiness probe for the Kibana container
func readinessProbe(useTLS bool) corev1.Probe {
Expand Down
5 changes: 1 addition & 4 deletions pkg/controller/license/license_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ type ReconcileLicenses struct {
checker license.Checker
}

// findLicense tries to find the best license available.
// findLicense tries to find the best Elastic stack license available.
func findLicense(c k8s.Client, checker license.Checker, minVersion *version.Version) (esclient.License, string, bool) {
licenseList, errs := license.EnterpriseLicensesOrErrors(c)
if len(errs) > 0 {
Expand Down Expand Up @@ -220,9 +220,6 @@ func (r *ReconcileLicenses) reconcileClusterLicense(cluster esv1.Elasticsearch)
return noResult, true, err
}
matchingSpec, parent, found := findLicense(r, r.checker, minVersion)
if err != nil {
return noResult, true, err
}
if !found {
// no license, delete cluster level licenses to revert to basic
log.V(1).Info("No enterprise license found. Attempting to remove cluster license secret", "namespace", cluster.Namespace, "es_name", cluster.Name)
Expand Down
Loading