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

Use enterprise_trial as the trial license type #2934

Merged
merged 12 commits into from
Apr 23, 2020
8 changes: 6 additions & 2 deletions cmd/licensing-info/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package main

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

Expand All @@ -25,7 +26,7 @@ import (
//
// Example of use:
//
// > go run cmd/licensing-info/main.go
// > go run cmd/licensing-info/main.go -operator-namespace <operator-namespace>
// {
// "timestamp": "2019-12-17T11:56:02+01:00",
// "license_level": "basic",
Expand All @@ -35,7 +36,10 @@ import (
//

func main() {
licensingInfo, err := license.NewResourceReporter(newK8sClient()).Get()
var operatorNamespace string
flag.StringVar(&operatorNamespace, "operator-namespace", "elastic-system", "indicates the namespace where the operator is deployed")
flag.Parse()
licensingInfo, err := license.NewResourceReporter(newK8sClient(), operatorNamespace).Get()
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 am not sure we need still need this tool.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed, maybe open an issue to revisit keeping it in there? I'm not sure how valuable it is

Copy link
Contributor

Choose a reason for hiding this comment

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

I left it because it was handy for testing the license reporting and in case someone wanted to get it in real time but it is not documented. So I don't really see any good reason to keep it.

if err != nil {
log.Fatal(err, "Failed to get licensing info")
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ func execute() {
go func() {
time.Sleep(10 * time.Second) // wait some arbitrary time for the manager to start
mgr.GetCache().WaitForCacheSync(nil) // wait until k8s client cache is initialized
r := licensing.NewResourceReporter(mgr.GetClient())
r.Start(operatorNamespace, licensing.ResourceReporterFrequency)
r := licensing.NewResourceReporter(mgr.GetClient(), operatorNamespace)
r.Start(licensing.ResourceReporterFrequency)
}()

log.Info("Starting the manager", "uuid", operatorInfo.OperatorUUID,
Expand Down
4 changes: 2 additions & 2 deletions docs/operating-eck/licensing.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ metadata:
name: eck-trial-license
namespace: elastic-system
labels:
license.k8s.elastic.co/type: enterprise-trial
license.k8s.elastic.co/type: enterprise_trial
annotations:
elastic.co/eula: accepted <1>
EOF
Expand All @@ -36,7 +36,7 @@ At the end of the trial period, the Platinum and Enterprise features operate in

[float]
== Add a license
If you have a valid Enterprise subscription you will receive a license as a JSON file.
If you have a valid Enterprise subscription or a trial license extension, you will receive a license as a JSON file.

NOTE: Please note that ECK will only accept Enterprise licenses. You can not apply a single Platinum or Gold cluster license to ECK.

Expand Down
5 changes: 1 addition & 4 deletions pkg/controller/common/license/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func NewLicenseChecker(client k8s.Client, operatorNamespace string) Checker {
}

func (lc *checker) publicKeyFor(l EnterpriseLicense) ([]byte, error) {
if !l.IsTrial() {
if !l.IsECKManagedTrial() {
return lc.publicKey, nil
}
var signatureSec corev1.Secret
Expand Down Expand Up @@ -87,9 +87,6 @@ func (lc *checker) EnterpriseFeaturesEnabled() (bool, error) {

// Valid returns true if the given Enterprise license is valid or an error if any.
func (lc *checker) Valid(l EnterpriseLicense) (bool, error) {
if l.IsTrial() {
return true, nil
}
pk, err := lc.publicKeyFor(l)
if err != nil {
return false, errors.Wrap(err, "while loading signature secret")
Expand Down
90 changes: 72 additions & 18 deletions pkg/controller/common/license/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
"github.com/elastic/cloud-on-k8s/pkg/utils/k8s"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)

const testNS = "test-system"

func TestChecker_EnterpriseFeaturesEnabled(t *testing.T) {
privKey, err := x509.ParsePKCS1PrivateKey(privateKeyFixture)
require.NoError(t, err)
Expand All @@ -25,10 +28,29 @@ func TestChecker_EnterpriseFeaturesEnabled(t *testing.T) {
signatureBytes, err := NewSigner(privKey).Sign(validLicenseFixture)
require.NoError(t, err)

trialState, err := NewTrialState()
require.NoError(t, err)
validTrialLicenseFixture := emptyTrialLicenseFixture
require.NoError(t, trialState.InitTrialLicense(&validTrialLicenseFixture))

validLegacyTrialFixture := EnterpriseLicense{
License: LicenseSpec{
Type: LicenseTypeLegacyTrial,
},
}
require.NoError(t, trialState.InitTrialLicense(&validLegacyTrialFixture))

expiredTrialLicense := validTrialLicenseFixture
expiredTrialLicense.License.ExpiryDateInMillis = chrono.ToMillis(time.Now().Add(-1 * time.Hour))
expiredTrialSignatureBytes, err := NewSigner(trialState.privateKey).Sign(expiredTrialLicense)
require.NoError(t, err)

statusSecret, err := ExpectedTrialStatus(testNS, types.NamespacedName{}, trialState)
require.NoError(t, err)

type fields struct {
initialObjects []runtime.Object
operatorNamespace string
publicKey []byte
initialObjects []runtime.Object
publicKey []byte
}
tests := []struct {
name string
Expand All @@ -39,27 +61,56 @@ func TestChecker_EnterpriseFeaturesEnabled(t *testing.T) {
{
name: "valid license: OK",
fields: fields{
initialObjects: asRuntimeObjects(validLicenseFixture, signatureBytes),
operatorNamespace: "test-system",
publicKey: publicKeyBytesFixture(t),
initialObjects: asRuntimeObjects(validLicenseFixture, signatureBytes),
publicKey: publicKeyBytesFixture(t),
},
want: true,
},
{
name: "valid trial: OK",
fields: fields{
initialObjects: []runtime.Object{asRuntimeObject(validTrialLicenseFixture), &statusSecret},
},
want: true,
wantErr: false,
},
{
name: "valid legacy trial: OK",
fields: fields{
initialObjects: []runtime.Object{asRuntimeObject(validTrialLicenseFixture), &statusSecret},
},
want: true,
wantErr: false,
},
{
name: "invalid trial: FAIL",
fields: fields{
initialObjects: []runtime.Object{asRuntimeObject(emptyTrialLicenseFixture), &statusSecret},
},
want: false,
wantErr: false,
},
pebrc marked this conversation as resolved.
Show resolved Hide resolved
{
name: "expired trial: FAIL",
fields: fields{
initialObjects: append(asRuntimeObjects(expiredTrialLicense, expiredTrialSignatureBytes), &statusSecret),
},
want: false,
wantErr: false,
},
{
name: "invalid signature: FAIL",
fields: fields{
initialObjects: asRuntimeObjects(validLicenseFixture, []byte{}),
operatorNamespace: "test-system",
publicKey: publicKeyBytesFixture(t),
initialObjects: asRuntimeObjects(validLicenseFixture, []byte{}),
publicKey: publicKeyBytesFixture(t),
},
want: false,
wantErr: false,
},
{
name: "no public key: FAIL",
fields: fields{
initialObjects: asRuntimeObjects(validLicenseFixture, signatureBytes),
operatorNamespace: "test-system",
initialObjects: asRuntimeObjects(validLicenseFixture, signatureBytes),
},
want: false,
wantErr: true,
Expand All @@ -69,7 +120,7 @@ func TestChecker_EnterpriseFeaturesEnabled(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
lc := &checker{
k8sClient: k8s.WrappedFakeClient(tt.fields.initialObjects...),
operatorNamespace: tt.fields.operatorNamespace,
operatorNamespace: testNS,
publicKey: tt.fields.publicKey,
}
got, err := lc.EnterpriseFeaturesEnabled()
Expand All @@ -93,11 +144,14 @@ func Test_CurrentEnterpriseLicense(t *testing.T) {
require.NoError(t, err)
validLicense := asRuntimeObjects(validLicenseFixture, signatureBytes)

validTrialLicenseFixture := trialLicenseFixture
validTrialLicenseFixture.License.ExpiryDateInMillis = chrono.ToMillis(time.Now().Add(1 * time.Hour))
trialSignatureBytes, err := NewSigner(privKey).Sign(validTrialLicenseFixture)
trialState, err := NewTrialState()
require.NoError(t, err)
validTrialLicenseFixture := emptyTrialLicenseFixture
require.NoError(t, trialState.InitTrialLicense(&validTrialLicenseFixture))
validTrialLicense := asRuntimeObject(validTrialLicenseFixture)

statusSecret, err := ExpectedTrialStatus(testNS, types.NamespacedName{}, trialState)
require.NoError(t, err)
validTrialLicense := asRuntimeObjects(validTrialLicenseFixture, trialSignatureBytes)

type fields struct {
initialObjects []runtime.Object
Expand Down Expand Up @@ -126,7 +180,7 @@ func Test_CurrentEnterpriseLicense(t *testing.T) {
{
name: "get valid trial enterprise license: OK",
fields: fields{
initialObjects: validTrialLicense,
initialObjects: []runtime.Object{validTrialLicense, &statusSecret},
operatorNamespace: "test-system",
publicKey: publicKeyBytesFixture(t),
},
Expand All @@ -137,7 +191,7 @@ func Test_CurrentEnterpriseLicense(t *testing.T) {
{
name: "get valid enterprise license among two licenses: OK",
fields: fields{
initialObjects: append(validLicense, validTrialLicense...),
initialObjects: append(validLicense, validTrialLicense),
operatorNamespace: "test-system",
publicKey: publicKeyBytesFixture(t),
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/controller/common/license/crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ func TrialLicense(c k8s.Client, nsn types.NamespacedName) (corev1.Secret, Enterp
}

// CreateTrialLicense creates en empty secret with the correct meta data to start an enterprise trial
func CreateTrialLicense(c k8s.Client, namespace string) error {
func CreateTrialLicense(c k8s.Client, nsn types.NamespacedName) error {
return c.Create(&corev1.Secret{
ObjectMeta: v1.ObjectMeta{
Name: string(LicenseTypeEnterpriseTrial),
Namespace: namespace,
Name: nsn.Name,
Namespace: nsn.Namespace,
Labels: map[string]string{
common.TypeLabelName: Type,
LicenseLabelType: string(LicenseTypeEnterpriseTrial),
Expand Down
8 changes: 3 additions & 5 deletions pkg/controller/common/license/detection.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ func isLicenseType(secret corev1.Secret, licenseType OperatorLicenseType) bool {

// IsEnterpriseTrial returns true if the given secret is a wrapper for an Enterprise Trial license
func IsEnterpriseTrial(secret corev1.Secret) bool {
return isLicenseType(secret, LicenseTypeEnterpriseTrial)
}

func IsEnterpriseLicense(secret corev1.Secret) bool {
return isLicenseType(secret, LicenseTypeEnterprise)
// we need to support legacy trial license secrets for backwards compatibility
return isLicenseType(secret, LicenseTypeEnterpriseTrial) || isLicenseType(secret, LicenseTypeLegacyTrial)
}

// IsOperatorLicense returns true if the given secret is a wrapper for an operator license.
func IsOperatorLicense(secret corev1.Secret) bool {
scope, hasLabel := secret.Labels[LicenseLabelScope]
return hasLabel && scope == string(LicenseScopeOperator)
Expand Down
36 changes: 20 additions & 16 deletions pkg/controller/common/license/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var (
}
)

var trialLicenseFixture = EnterpriseLicense{
var emptyTrialLicenseFixture = EnterpriseLicense{
License: LicenseSpec{
Type: LicenseTypeEnterpriseTrial,
},
Expand All @@ -60,27 +60,31 @@ func withSignature(l EnterpriseLicense, sig []byte) EnterpriseLicense {
return l
}

func asRuntimeObjects(l EnterpriseLicense, sig []byte) []runtime.Object {
bytes, err := json.Marshal(withSignature(l, sig))
func asRuntimeObject(l EnterpriseLicense) runtime.Object {
bytes, err := json.Marshal(l)
if err != nil {
panic(err)
}

return []runtime.Object{
&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-system",
Name: fmt.Sprintf("test-%s-license", string(l.License.Type)),
Labels: map[string]string{
common.TypeLabelName: Type,
LicenseLabelScope: string(LicenseScopeOperator),
LicenseLabelType: string(l.License.Type),
},
},
Data: map[string][]byte{
FileName: bytes,
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Namespace: "test-system",
Name: fmt.Sprintf("test-%s-license", string(l.License.Type)),
Labels: map[string]string{
common.TypeLabelName: Type,
LicenseLabelScope: string(LicenseScopeOperator),
LicenseLabelType: string(l.License.Type),
},
},
Data: map[string][]byte{
FileName: bytes,
},
}
}

func asRuntimeObjects(l EnterpriseLicense, sig []byte) []runtime.Object {
return []runtime.Object{
asRuntimeObject(withSignature(l, sig)),
}
}

Expand Down
16 changes: 12 additions & 4 deletions pkg/controller/common/license/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ type OperatorLicenseType string

const (
LicenseTypeEnterprise OperatorLicenseType = "enterprise"
LicenseTypeEnterpriseTrial OperatorLicenseType = "enterprise-trial"
LicenseTypeEnterpriseTrial OperatorLicenseType = "enterprise_trial"
// LicenseTypeLegacyTrial earlier versions of ECK used this as the trial identifier
LicenseTypeLegacyTrial OperatorLicenseType = "enterprise-trial"
)

type ElasticsearchLicense struct {
Expand Down Expand Up @@ -47,8 +49,9 @@ type LicenseSpec struct {

// EnterpriseLicenseTypeOrder license types mapped to ints in increasing order of feature sets for sorting purposes.
var EnterpriseLicenseTypeOrder = map[OperatorLicenseType]int{
LicenseTypeEnterpriseTrial: 0,
LicenseTypeEnterprise: 1,
LicenseTypeLegacyTrial: 0,
LicenseTypeEnterpriseTrial: 1,
LicenseTypeEnterprise: 2,
}

// StartTime is the date as of which this license is valid.
Expand All @@ -69,7 +72,12 @@ func (l EnterpriseLicense) IsValid(instant time.Time) bool {

// IsTrial returns true if this is a self-generated trial license.
func (l EnterpriseLicense) IsTrial() bool {
return l.License.Type == LicenseTypeEnterpriseTrial
return l.License.Type == LicenseTypeEnterpriseTrial || l.License.Type == LicenseTypeLegacyTrial
}

// IsECKManagedTrial returns true if this license has been issued by ECK or if this is an empty license that ECK can fill in.
func (l EnterpriseLicense) IsECKManagedTrial() bool {
return l.License.Issuer == ECKLicenseIssuer || l.License.Issuer == ""
}

// IsMissingFields returns an error if any of the required fields are missing. Expected state on trial licenses.
Expand Down
Loading