Skip to content

Commit

Permalink
Add tool to upgrade ACLs. (#5016) (#5065)
Browse files Browse the repository at this point in the history
Add dgraph upgrade tool to upgrade the ACL data format.

Co-authored-by: Animesh Chandra Pathak <[email protected]>
Co-authored-by: Aman Mangal <[email protected]>
  • Loading branch information
3 people authored Mar 31, 2020
1 parent 065d860 commit e604b91
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 8 deletions.
9 changes: 5 additions & 4 deletions dgraph/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@ import (
"os"
"strings"

"github.com/dgraph-io/dgraph/dgraph/cmd/debuginfo"
"github.com/dgraph-io/dgraph/dgraph/cmd/migrate"

"github.com/dgraph-io/dgraph/dgraph/cmd/alpha"
"github.com/dgraph-io/dgraph/dgraph/cmd/bulk"
"github.com/dgraph-io/dgraph/dgraph/cmd/cert"
"github.com/dgraph-io/dgraph/dgraph/cmd/conv"
"github.com/dgraph-io/dgraph/dgraph/cmd/counter"
"github.com/dgraph-io/dgraph/dgraph/cmd/debug"
"github.com/dgraph-io/dgraph/dgraph/cmd/debuginfo"
"github.com/dgraph-io/dgraph/dgraph/cmd/live"
"github.com/dgraph-io/dgraph/dgraph/cmd/migrate"
"github.com/dgraph-io/dgraph/dgraph/cmd/version"
"github.com/dgraph-io/dgraph/dgraph/cmd/zero"
"github.com/dgraph-io/dgraph/upgrade"
"github.com/dgraph-io/dgraph/x"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -74,7 +75,7 @@ var rootConf = viper.New()
// subcommands initially contains all default sub-commands.
var subcommands = []*x.SubCommand{
&bulk.Bulk, &cert.Cert, &conv.Conv, &live.Live, &alpha.Alpha, &zero.Zero, &version.Version,
&debug.Debug, &counter.Increment, &migrate.Migrate, &debuginfo.DebugInfo,
&debug.Debug, &counter.Increment, &migrate.Migrate, &debuginfo.DebugInfo, &upgrade.Upgrade,
}

func initCmds() {
Expand Down
2 changes: 1 addition & 1 deletion edgraph/access_ee.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,7 @@ func isAclPredMutation(nquads []*api.NQuad) bool {
var aclsToChange []acl.Acl
err := json.Unmarshal(aclBytes.BytesVal, &aclsToChange)
if err != nil {
glog.Errorf(fmt.Sprintf("Unable to unmalshal bytes under the dgraph.group.acl "+
glog.Errorf(fmt.Sprintf("Unable to unmarshal bytes under the dgraph.group.acl "+
"predicate: %v", err))
continue
}
Expand Down
177 changes: 177 additions & 0 deletions upgrade/acl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright 2020 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 upgrade

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/dgraph-io/dgo/v2"
"github.com/dgraph-io/dgo/v2/protos/api"
"google.golang.org/grpc"
)

const (
oldACLQuery = `
{
rules(func: type(Group)) {
uid
dgraph.group.acl
}
}
`
)

type group struct {
UID string `json:"uid"`
ACL string `json:"dgraph.group.acl,omitempty"`
}

type rule struct {
Predicate string `json:"predicate,omitempty"`
Permission int `json:"perm,omitempty"`
}

type rules []rule

func upgradeACLRules() error {
alpha := Upgrade.Conf.GetString("alpha")
userName := Upgrade.Conf.GetString("user")
password := Upgrade.Conf.GetString("password")
deleteOld := Upgrade.Conf.GetBool("deleteOld")

// TODO(Aman): add TLS configuration.
conn, err := grpc.Dial(alpha, grpc.WithInsecure())
if err != nil {
return fmt.Errorf("unable to connect to Dgraph cluster: %w", err)
}
defer conn.Close()

dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

// login to cluster
if err := dg.Login(ctx, userName, password); err != nil {
return fmt.Errorf("unable to login to Dgraph cluster: %w", err)
}

ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
resp, err := dg.NewReadOnlyTxn().Query(ctx, oldACLQuery)
if err != nil {
return fmt.Errorf("unable to query old ACL rules: %w", err)
}

data := make(map[string][]group)
if err := json.Unmarshal(resp.Json, &data); err != nil {
return fmt.Errorf("unable to unmarshal old ACLs: %w", err)
}

groups, ok := data["rules"]
if !ok {
return fmt.Errorf("Unable to parse ACLs: %v", string(resp.Json))
}

counter := 1
var nquads []*api.NQuad
for _, group := range groups {
if len(group.ACL) == 0 {
continue
}

var rs rules
if err := json.Unmarshal([]byte(group.ACL), &rs); err != nil {
return fmt.Errorf("Unable to unmarshal ACL: %v :: %w", string(group.ACL), err)
}

for _, r := range rs {
newRuleStr := fmt.Sprintf("_:newrule%d", counter)
nquads = append(nquads, &api.NQuad{
Subject: newRuleStr,
Predicate: "dgraph.rule.predicate",
ObjectValue: &api.Value{
Val: &api.Value_StrVal{StrVal: r.Predicate},
},
})

nquads = append(nquads, &api.NQuad{
Subject: newRuleStr,
Predicate: "dgraph.rule.permission",
ObjectValue: &api.Value{
Val: &api.Value_IntVal{IntVal: int64(r.Permission)},
},
})

nquads = append(nquads, &api.NQuad{
Subject: group.UID,
Predicate: "dgraph.acl.rule",
ObjectId: newRuleStr,
})

counter++
}
}

// Nothing to do.
if len(nquads) == 0 {
return fmt.Errorf("no old rules found in the cluster")
}

if err := mutateACL(dg, nquads); err != nil {
return fmt.Errorf("error upgrading ACL rules: %w", err)
}
fmt.Println("Successfully upgraded ACL rules.")

ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if deleteOld {
err = dg.Alter(ctx, &api.Operation{
DropOp: api.Operation_ATTR,
DropValue: "dgraph.group.acl",
})
if err != nil {
return fmt.Errorf("error deleting old acl predicates: %w", err)
}
fmt.Println("Successfully deleted old rules.")
}

return nil
}

func mutateACL(dg *dgo.Dgraph, nquads []*api.NQuad) error {
var err error
for i := 0; i < 3; i++ {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

_, err = dg.NewTxn().Mutate(ctx, &api.Mutation{
Set: nquads,
CommitNow: true,
})
if err != nil {
fmt.Printf("error in running mutation, retrying: %v\n", err)
continue
}

return nil
}

return err
}
58 changes: 58 additions & 0 deletions upgrade/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2020 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 upgrade

import (
"fmt"
"os"

"github.com/dgraph-io/dgraph/x"
"github.com/spf13/cobra"
)

var (
// Upgrade is the sub-command used to upgrade dgraph cluster.
Upgrade x.SubCommand
)

func init() {
Upgrade.Cmd = &cobra.Command{
Use: "upgrade",
Short: "Run the Dgraph upgrade tool",
Run: func(cmd *cobra.Command, args []string) {
run()
},
}

flag := Upgrade.Cmd.Flags()
flag.Bool("acl", false, "upgrade ACL from v1.2.2 to >v20.03.0")
flag.StringP("alpha", "a", "127.0.0.1:9080", "Dgraph Alpha gRPC server address")
flag.StringP("user", "u", "", "Username if login is required.")
flag.StringP("password", "p", "", "Password of the user.")
flag.BoolP("deleteOld", "d", true, "Delete the older ACL predicates")
}

func run() {
if !Upgrade.Conf.GetBool("acl") {
fmt.Fprint(os.Stderr, "Error! we only support acl upgrade as of now.\n")
return
}

if err := upgradeACLRules(); err != nil {
fmt.Fprintln(os.Stderr, "Error in upgrading ACL!", err)
}
}
20 changes: 17 additions & 3 deletions wiki/content/deploy/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ If the `--tls_client_auth` option is set to `REQUIREANY` or `REQUIREANDVERIFY`
in addition to the `--cacert` option, also use the `--cert` and `--key` options.
For instance (for an export request):

```
```
curl --cacert ./tls/ca.crt --cert ./tls/node.crt --key ./tls/node.key https://localhost:8080/admin/export
```

Expand Down Expand Up @@ -2248,13 +2248,27 @@ Doing periodic exports is always a good idea. This is particularly useful if you

These steps are necessary because Dgraph's underlying data format could have changed, and reloading the export avoids encoding incompatibilities.

Blue-green deployment is a common approach to minimize downtime during the upgrade process.
This approach involves switching your application to read-only mode. To make sure that no mutations are executed during the maintenance window you can
Blue-green deployment is a common approach to minimize downtime during the upgrade process.
This approach involves switching your application to read-only mode. To make sure that no mutations are executed during the maintenance window you can
do a rolling restart of all your Alpha using the option `--mutations disallow` when you restart the Alphas. This will ensure the cluster is in read-only mode.

At this point your application can still read from the old cluster and you can perform the steps 4. and 5. described above.
When the new cluster (that uses the upgraded version of Dgraph) is up and running, you can point your application to it, and shutdown the old cluster.

#### Upgrading from v1.2.2 to v20.03.0 for Enterprise Customers

1. Use [binary]({{< relref "#binary-backups">}}) backup to export data from old cluster
2. Ensure it is successful
3. [Shutdown Dgraph]({{< relref "#shutting-down-database" >}}) and wait for all writes to complete
4. Upgrade `dgraph` binary to `v20.03.0`
5. [Restore]({{< relref "#restore-from-backup">}}) from the backups using upgraded `dgraph` binary
6. Start a new Dgraph cluster using the restored data directories
7. Upgrade ACL data using the following command:

```
dgraph upgrade --acl -a localhost:9080 -u groot -p password
```

{{% notice "note" %}}
If you are upgrading from v1.0, please make sure you follow the schema migration steps described in [this section](/howto/#schema-types-scalar-uid-and-list-uid).
{{% /notice %}}
Expand Down

0 comments on commit e604b91

Please sign in to comment.