From fe6b7f2675e65f322a96a4dd99123224c1366698 Mon Sep 17 00:00:00 2001 From: Bill Katz Date: Fri, 14 May 2021 18:48:44 -0400 Subject: [PATCH] add rpc commands for make-master and hide-branch --- datastore/datastore.go | 22 ++++++++ datastore/repo_local.go | 122 ++++++++++++++++++++++++++++++++++++++++ server/rpc.go | 30 ++++++++++ 3 files changed, 174 insertions(+) diff --git a/datastore/datastore.go b/datastore/datastore.go index d2c4cae4..ee3e589b 100644 --- a/datastore/datastore.go +++ b/datastore/datastore.go @@ -241,6 +241,28 @@ func NewVersion(parent dvid.UUID, note string, branchname string, assign *dvid.U return manager.newVersion(parent, note, branchname, assign) } +// MakeMaster makes the branch at given UUID (that node and all its children) +// the new master branch and renames the old master branch to the given +// branch name. NOTE: This command will fail if the given UUID is not +// a node that is directly branched off master. +func MakeMaster(newMasterNode dvid.UUID, oldMasterBranchName string) error { + if manager == nil { + return ErrManagerNotInitialized + } + return manager.makeMaster(newMasterNode, oldMasterBranchName) +} + +// HideBranch makes the branch at given UUID (that node and all its children) +// the new master branch and renames the old master branch to the given +// branch name. NOTE: This command will fail if the given UUID is not +// a node that is directly branched off master. +func HideBranch(uuid dvid.UUID, branchName string) error { + if manager == nil { + return ErrManagerNotInitialized + } + return manager.hideBranch(uuid, branchName) +} + // GetParents returns the parent nodes of the given version id. func GetParentsByVersion(v dvid.VersionID) ([]dvid.VersionID, error) { if manager == nil { diff --git a/datastore/repo_local.go b/datastore/repo_local.go index 108d25a5..83e9725c 100644 --- a/datastore/repo_local.go +++ b/datastore/repo_local.go @@ -1592,6 +1592,109 @@ func (m *repoManager) commit(uuid dvid.UUID, note string, log []string) error { return r.save() } +func (m *repoManager) hideBranch(uuid dvid.UUID, branch string) error { + if branch == "" { + return fmt.Errorf("cannot hide master branch") + } + r, err := m.repoFromUUID(uuid) + if err != nil { + return err + } + m.repoMutex.Lock() + r.Lock() + del_set := make(map[dvid.VersionID]struct{}) + for v, node := range r.dag.nodes { + if node.branch == branch { + del_set[v] = struct{}{} + del_uuid := m.versionToUUID[v] + delete(m.versionToUUID, v) + delete(m.uuidToVersion, del_uuid) + delete(r.dag.nodes, v) + delete(m.repos, del_uuid) + } + } + for _, node := range r.dag.nodes { + var children []dvid.VersionID + for _, cv := range node.children { + if _, found := del_set[cv]; !found { + children = append(children, cv) + } + } + if len(children) < len(node.children) { + node.children = children + } + } + r.Unlock() + m.repoMutex.Unlock() + return r.save() +} + +func (m *repoManager) makeMaster(newMasterUUID dvid.UUID, oldMasterBranchName string) error { + if oldMasterBranchName == "" { + return fmt.Errorf("renamed master branch cannot be empty string") + } + r, err := m.repoFromUUID(newMasterUUID) + if err != nil { + return err + } + newMasterV, err := m.versionFromUUID(newMasterUUID) + if err != nil { + return err + } + + r.RLock() + defer r.RUnlock() + newMasterNode, found := r.dag.nodes[newMasterV] + if !found { + return fmt.Errorf("unable to find node with version %d, UUID %s", newMasterV, newMasterUUID) + } + if newMasterNode.branch == "" { + return fmt.Errorf("designated node is already on the master branch") + } + + // Get master branch that must be sibling. + if len(newMasterNode.parents) == 0 { + return fmt.Errorf("given UUID must be the first node of the branch to make master") + } + parentV := newMasterNode.parents[0] + oldMasterNode, err := r.getChildBranchNode(parentV, "") + if err != nil { + return err + } + if oldMasterNode == nil { + return fmt.Errorf("no sibling master branch found for UUID %s", newMasterUUID) + } + + // Change the master nodes to the appropriate rename + for { + oldMasterNode.branch = oldMasterBranchName + childNode, err := r.getChildBranchNode(oldMasterNode.version, "") + if err != nil { + return err + } + if childNode == nil { + break + } + oldMasterNode = childNode + } + + // Change the new master nodes to the master branch. + oldBranchName := newMasterNode.branch + for { + newMasterNode.branch = "" + childNode, err := r.getChildBranchNode(newMasterNode.version, oldBranchName) + if err != nil { + return err + } + if childNode == nil { + break + } + newMasterNode = childNode + } + + return r.save() +} + // newVersion creates a new version as a child of the given parent. If the // assign parameter is not nil, the new node is given the UUID. func (m *repoManager) newVersion(parent dvid.UUID, note string, branchname string, assign *dvid.UUID) (dvid.UUID, error) { @@ -2735,6 +2838,25 @@ func (r *repoT) versionSet() map[dvid.VersionID]struct{} { return vset } +// get the given branch child from this node. +func (r *repoT) getChildBranchNode(parentV dvid.VersionID, branch string) (branchNode *nodeT, err error) { + parent, found := r.dag.nodes[parentV] + if !found { + return nil, ErrInvalidVersion + } + for _, v := range parent.children { + node, found := r.dag.nodes[v] + if !found { + continue + } + if node.branch == branch { + branchNode = node + break + } + } + return +} + // -------------------------------------- // DataAvail gives the availability of data within a node or whether parent nodes diff --git a/server/rpc.go b/server/rpc.go index 89a28f03..b5768f19 100644 --- a/server/rpc.go +++ b/server/rpc.go @@ -60,6 +60,20 @@ DANGEROUS COMMANDS (only available via command line) Delete the given data instance. + repo make-master + + Makes the branch at given UUID (that node and all its children) the + new master branch and renames the old master branch to the given + branch name. NOTE: This command will fail if the given UUID is not + a node that is directly branched off master. + + repo hide-branch + + Removes metadata for the given branch so that it is not visible. + This does not remove the key-value pairs associated wiith the given + nodes, since doing so would be very expensive. Instead, the old + key values aren't accessible. + EXPERIMENTAL COMMANDS @@ -621,6 +635,22 @@ func handleCommand(cmd *datastore.Request) (reply *datastore.Response, err error } reply.Text = fmt.Sprintf("Limited metadata versions for repo %s\n", uuid) + case "make-master": + var newMasterBranchName string + cmd.CommandArgs(3, &newMasterBranchName) + if err = datastore.MakeMaster(uuid, newMasterBranchName); err != nil { + dvid.Errorf("make-master error: %v\n", err) + } + reply.Text = fmt.Sprintf("Made branch from %s the new master\n", uuid) + + case "hide-branch": + var branchName string + cmd.CommandArgs(3, &branchName) + if err = datastore.HideBranch(uuid, branchName); err != nil { + dvid.Errorf("make-master error: %v\n", err) + } + reply.Text = fmt.Sprintf("Hid branch %s in repo with UUID %s\n", branchName, uuid) + case "push": var target string cmd.CommandArgs(3, &target)