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

Don't apply enterprise state for oss builds. #3847

Merged
merged 8 commits into from
Aug 27, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
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().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)
timeToExpire := expiry.Sub(time.Now())
// 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().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)
state.License.Enabled = time.Now().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