forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 1
/
license.go
102 lines (90 loc) · 2.78 KB
/
license.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright 2017 The Cockroach Authors.
//
// Licensed as a CockroachDB Enterprise file under the Cockroach Community
// License (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// https://github.com/cockroachdb/cockroach/blob/master/LICENSE
package licenseccl
import (
"bytes"
"encoding/base64"
"strings"
"time"
"github.com/pkg/errors"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/cockroachdb/cockroach/pkg/util/uuid"
)
// LicensePrefix is a prefix on license strings to make them easily recognized.
const LicensePrefix = "crl-0-"
// Encode serializes the License as a base64 string.
func (l License) Encode() (string, error) {
bytes, err := l.Marshal()
if err != nil {
return "", err
}
return LicensePrefix + base64.RawStdEncoding.EncodeToString(bytes), nil
}
// Decode attempts to read a base64 encoded License.
func Decode(s string) (*License, error) {
if s == "" {
return nil, nil
}
if !strings.HasPrefix(s, LicensePrefix) {
return nil, errors.New("invalid license string")
}
s = strings.TrimPrefix(s, LicensePrefix)
data, err := base64.RawStdEncoding.DecodeString(s)
if err != nil {
return nil, errors.Wrap(err, "invalid license string")
}
var lic License
if err := lic.Unmarshal(data); err != nil {
return nil, errors.Wrap(err, "invalid license string")
}
return &lic, nil
}
// Check returns an error if the license is empty or not currently valid.
func (l *License) Check(at time.Time, cluster uuid.UUID, org, feature string) error {
if l == nil {
// TODO(dt): link to some stable URL that then redirects to a helpful page
// that explains what to do here.
link := "https://cockroachlabs.com/pricing?cluster="
return errors.Errorf(
"use of %s requires an enterprise license. "+
"see %s%s for details on how to enable enterprise features",
feature,
link,
cluster.String(),
)
}
// We extend some grace period to enterprise license holders rather than
// suddenly throwing errors at them.
if l.ValidUntilUnixSec > 0 && l.Type != License_Enterprise {
if expiration := timeutil.Unix(l.ValidUntilUnixSec, 0); at.After(expiration) {
return errors.Errorf("license expired at %s", expiration.String())
}
}
if l.ClusterID == nil {
if strings.EqualFold(l.OrganizationName, org) {
return nil
}
return errors.Errorf("license valid only for %q", l.OrganizationName)
}
for _, c := range l.ClusterID {
if cluster == c {
return nil
}
}
// no match, so compose an error message.
var matches bytes.Buffer
for i, c := range l.ClusterID {
if i > 0 {
matches.WriteString(", ")
}
matches.WriteString(c.String())
}
return errors.Errorf(
"license for cluster(s) %s is not valid for cluster %s", matches.String(), cluster.String(),
)
}