Skip to content

Commit

Permalink
Don't apply license state for oss builds. (#3847)
Browse files Browse the repository at this point in the history
This change ensure that license state is only applied and warnings about expiry only printed for `!oss` builds.
  • Loading branch information
pawanrawal authored Aug 27, 2019
1 parent 00a2914 commit 98880f8
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 121 deletions.
34 changes: 0 additions & 34 deletions dgraph/cmd/zero/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
package zero

import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net"
"net/http"
"strconv"
Expand Down Expand Up @@ -234,38 +232,6 @@ func (st *state) getState(w http.ResponseWriter, r *http.Request) {
}
}

// applyEnterpriseLicense accepts a PGP message as a POST request body, verifies that it was
// signed using our private key and applies the license which has maxNodes and Expiry to the
// cluster.
func (st *state) applyEnterpriseLicense(w http.ResponseWriter, r *http.Request) {
x.AddCorsHeaders(w)
if r.Method == "OPTIONS" {
return
}
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidMethod, "Invalid method")
return
}

w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidRequest, err.Error())
return
}

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
if err := st.zero.applyEnterpriseLicense(ctx, bytes.NewReader(b)); err != nil {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidRequest, err.Error())
return
}
x.SetStatus(w, x.Success, "Done")
}

func (st *state) serveHTTP(l net.Listener) {
srv := &http.Server{
ReadTimeout: 10 * time.Second,
Expand Down
39 changes: 39 additions & 0 deletions dgraph/cmd/zero/license.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// +build oss

/*
* Copyright 2018 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package zero

import (
"net/http"

"github.com/dgraph-io/badger/y"
)

// dummy function as enterprise features are not available in oss binary.
func (n *node) proposeTrialLicense() error {
return nil
}

// periodically checks the validity of the enterprise license and updates the membership state.
func (n *node) updateEnterpriseState(closer *y.Closer) {
closer.Done()
}

func (st *state) applyEnterpriseLicense(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
}
131 changes: 131 additions & 0 deletions dgraph/cmd/zero/license_ee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// +build !oss

/*
* Copyright 2018 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Dgraph 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/dgraph-io/dgraph/blob/master/licenses/DCL.txt
*/

package zero

import (
"bytes"
"io/ioutil"
"math"
"net/http"
"time"

"github.com/dgraph-io/badger/y"
"github.com/dgraph-io/dgraph/protos/pb"
"github.com/dgraph-io/dgraph/x"
humanize "github.com/dustin/go-humanize"
"github.com/gogo/protobuf/proto"
"github.com/golang/glog"
"golang.org/x/net/context"
)

// proposeTrialLicense proposes an enterprise license valid for 30 days.
func (n *node) proposeTrialLicense() error {
// Apply enterprise license valid for 30 days from now.
proposal := &pb.ZeroProposal{
License: &pb.License{
MaxNodes: math.MaxUint64,
ExpiryTs: time.Now().UTC().Add(humanize.Month).Unix(),
},
}
err := n.proposeAndWait(context.Background(), proposal)
if err != nil {
return err

}
glog.Infof("Enterprise state proposed to the cluster: %v", proposal)
return nil
}

func (s *Server) license() *pb.License {
s.RLock()
defer s.RUnlock()
return proto.Clone(s.state.GetLicense()).(*pb.License)
}

func (s *Server) expireLicense() {
s.Lock()
defer s.Unlock()
s.state.License.Enabled = false
}

// periodically checks the validity of the enterprise license and
// 1. Sets license.Enabled to false in membership state if license has expired.
// 2. Prints out warning once every day a week before the license is set to expire.
func (n *node) updateEnterpriseState(closer *y.Closer) {
defer closer.Done()

interval := 5 * time.Second
ticker := time.NewTicker(interval)
defer ticker.Stop()

intervalsInDay := int64(24*time.Hour) / int64(interval)
var counter int64
for {
select {
case <-ticker.C:
counter++
license := n.server.license()
if !license.GetEnabled() {
continue
}

expiry := time.Unix(license.GetExpiryTs(), 0).UTC()
timeToExpire := expiry.Sub(time.Now().UTC())
// We only want to print this log once a day.
if counter%intervalsInDay == 0 && timeToExpire > 0 && timeToExpire < humanize.Week {
glog.Warningf("Enterprise license is going to expire in %s.", humanize.Time(expiry))
}

active := time.Now().UTC().Before(expiry)
if !active {
n.server.expireLicense()
glog.Warningf("Enterprise license has expired and enterprise features would be " +
"disabled now. Talk to us at [email protected] to get a new license.")
}
case <-closer.HasBeenClosed():
return
}
}
}

// applyEnterpriseLicense accepts a PGP message as a POST request body, verifies that it was
// signed using our private key and applies the license which has maxNodes and Expiry to the
// cluster.
func (st *state) applyEnterpriseLicense(w http.ResponseWriter, r *http.Request) {
x.AddCorsHeaders(w)
if r.Method == "OPTIONS" {
return
}
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidMethod, "Invalid method")
return
}

w.Header().Set("Content-Type", "application/json")
b, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidRequest, err.Error())
return
}

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
if err := st.zero.applyLicense(ctx, bytes.NewReader(b)); err != nil {
w.WriteHeader(http.StatusBadRequest)
x.SetStatus(w, x.ErrorInvalidRequest, err.Error())
return
}
x.SetStatus(w, x.Success, "Done")
}
51 changes: 6 additions & 45 deletions dgraph/cmd/zero/raft.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import (
"github.com/dgraph-io/dgraph/protos/pb"
"github.com/dgraph-io/dgraph/x"
farm "github.com/dgryski/go-farm"
humanize "github.com/dustin/go-humanize"
"github.com/golang/glog"
"github.com/google/uuid"
"github.com/pkg/errors"
Expand Down Expand Up @@ -370,6 +369,9 @@ func (n *node) applyProposal(e raftpb.Entry) (string, error) {
return p.Key, errInvalidProposal
}
state.License = p.License
// Check expiry and set enabled accordingly.
expiry := time.Unix(state.License.ExpiryTs, 0).UTC()
state.License.Enabled = time.Now().UTC().Before(expiry)
}

if p.MaxLeaseId > state.MaxLeaseId {
Expand Down Expand Up @@ -516,25 +518,8 @@ func (n *node) initAndStartNode() error {
time.Sleep(3 * time.Second)
}

// Apply enterprise license valid for 30 days from now.
proposal := &pb.ZeroProposal{
License: &pb.License{
MaxNodes: math.MaxUint64,
ExpiryTs: time.Now().Add(humanize.Month).Unix(),
},
}
for {
err := n.proposeAndWait(context.Background(), proposal)
if err == nil {
glog.Infof("Enterprise state proposed to the cluster: %v", proposal)
return
}
if err == errInvalidProposal {
glog.Errorf("invalid proposal error while proposing enteprise state")
return
}
glog.Errorf("While proposing enterprise state: %v. Retrying...", err)
time.Sleep(3 * time.Second)
if err := n.proposeTrialLicense(); err != nil {
glog.Errorf("while proposing trial license to cluster: %v", err)
}
}()
}
Expand All @@ -544,29 +529,6 @@ func (n *node) initAndStartNode() error {
return nil
}

// periodically checks the validity of the enterprise license and updates the membership state.
func (n *node) updateEnterpriseStatePeriodically(closer *y.Closer) {
defer closer.Done()

ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()

dailyTicker := time.NewTicker(humanize.Day)
defer dailyTicker.Stop()

n.server.updateEnterpriseState()
for {
select {
case <-ticker.C:
n.server.updateEnterpriseState()
case <-dailyTicker.C:
n.server.licenseExpiryWarning()
case <-closer.HasBeenClosed():
return
}
}
}

func (n *node) updateZeroMembershipPeriodically(closer *y.Closer) {
defer closer.Done()
ticker := time.NewTicker(10 * time.Second)
Expand All @@ -576,7 +538,6 @@ func (n *node) updateZeroMembershipPeriodically(closer *y.Closer) {
select {
case <-ticker.C:
n.server.updateZeroLeader()

case <-closer.HasBeenClosed():
return
}
Expand Down Expand Up @@ -672,7 +633,7 @@ func (n *node) Run() {
}()

go n.snapshotPeriodically(closer)
go n.updateEnterpriseStatePeriodically(closer)
go n.updateEnterpriseState(closer)
go n.updateZeroMembershipPeriodically(closer)
go n.checkQuorum(closer)
go n.RunReadIndexLoop(closer, readStateCh)
Expand Down
43 changes: 1 addition & 42 deletions dgraph/cmd/zero/zero.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"sync"
"time"

"github.com/dustin/go-humanize"

otrace "go.opencensus.io/trace"
"golang.org/x/net/context"

Expand Down Expand Up @@ -275,45 +273,6 @@ func (s *Server) updateZeroLeader() {
}
}

// updateEnterpriseState periodically checks the validity of the enterprise license
// based on its expiry.
func (s *Server) updateEnterpriseState() {
s.Lock()
defer s.Unlock()

// Return early if license is not enabled. This would happen when user didn't supply us a
// license file yet.
if s.state.GetLicense() == nil {
return
}

enabled := s.state.GetLicense().GetEnabled()
expiry := time.Unix(s.state.License.ExpiryTs, 0)
s.state.License.Enabled = time.Now().Before(expiry)
if enabled && !s.state.License.Enabled {
// License was enabled earlier and has just now been disabled.
glog.Infof("Enterprise license has expired and enterprise features would be disabled now. " +
"Talk to us at [email protected] to get a new license.")
}
}

// Prints out an info log about the expiry of the license if its about to expire in less than a
// week.
func (s *Server) licenseExpiryWarning() {
s.RLock()
defer s.RUnlock()

if s.state.GetLicense() == nil {
return
}
enabled := s.state.GetLicense().GetEnabled()
expiry := time.Unix(s.state.License.ExpiryTs, 0)
timeToExpire := expiry.Sub(time.Now())
if enabled && timeToExpire > 0 && timeToExpire < humanize.Week {
glog.Infof("Enterprise license is going to expire in %s.", humanize.Time(expiry))
}
}

func (s *Server) removeZero(nodeId uint64) {
s.Lock()
defer s.Unlock()
Expand Down Expand Up @@ -786,7 +745,7 @@ func (s *Server) latestMembershipState(ctx context.Context) (*pb.MembershipState
return ms, nil
}

func (s *Server) applyEnterpriseLicense(ctx context.Context, signedData io.Reader) error {
func (s *Server) applyLicense(ctx context.Context, signedData io.Reader) error {
var l license
if err := verifySignature(signedData, strings.NewReader(publicKey), &l); err != nil {
return errors.Wrapf(err, "while extracting enterprise details from the license")
Expand Down

0 comments on commit 98880f8

Please sign in to comment.