added hash check for node edit
Signed-off-by: Christian Goll <cgoll@suse.de>
This commit is contained in:
@@ -35,6 +35,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) error {
|
||||
set := wwapiv1.NodeAddParameter{
|
||||
NodeConfYaml: string(buffer[:]),
|
||||
NodeNames: args,
|
||||
Force: true,
|
||||
}
|
||||
return apinode.NodeAdd(&set)
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ var (
|
||||
},
|
||||
}
|
||||
SetYes bool
|
||||
SetForce bool // currently unused
|
||||
SetForce bool // no hash checking, so always using force
|
||||
)
|
||||
|
||||
func init() {
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetForce, "force", "f", false, "Force node delete")
|
||||
SetForce = true
|
||||
baseCmd.PersistentFlags().BoolVarP(&SetYes, "yes", "y", false, "Set 'yes' to all questions asked")
|
||||
|
||||
}
|
||||
|
||||
@@ -88,12 +88,20 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
if !yes {
|
||||
break
|
||||
}
|
||||
err = apinode.NodeDelete(&wwapiv1.NodeDeleteParameter{NodeNames: nodeList, Force: true})
|
||||
err = apinode.NodeDelete(&wwapiv1.NodeDeleteParameter{
|
||||
NodeNames: nodeList,
|
||||
Hash: nodeListMsg.Hash,
|
||||
})
|
||||
if err != nil {
|
||||
wwlog.Verbose("Problem deleting nodes before modification %s")
|
||||
wwlog.Error("Problem deleting nodes before modification: %s", err)
|
||||
return err
|
||||
}
|
||||
buffer, _ = yaml.Marshal(modifiedNodeMap)
|
||||
err = apinode.NodeAddFromYaml(&wwapiv1.NodeYaml{NodeConfMapYaml: string(buffer)})
|
||||
newHash := apinode.Hash()
|
||||
err = apinode.NodeAddFromYaml(&wwapiv1.NodeYaml{
|
||||
NodeConfMapYaml: string(buffer),
|
||||
Hash: newHash.Hash,
|
||||
})
|
||||
if err != nil {
|
||||
wwlog.Error("Got following problem when writing back yaml: %s", err)
|
||||
os.Exit(1)
|
||||
|
||||
@@ -24,6 +24,7 @@ func FindAllNodeConfs() *wwapiv1.NodeYaml {
|
||||
buffer, _ := yaml.Marshal(nodeMap)
|
||||
retVal := wwapiv1.NodeYaml{
|
||||
NodeConfMapYaml: string(buffer),
|
||||
Hash: nodeDB.StringHash(),
|
||||
}
|
||||
return &retVal
|
||||
}
|
||||
@@ -42,6 +43,7 @@ func FilteredNodes(nodeList *wwapiv1.NodeList) *wwapiv1.NodeYaml {
|
||||
buffer, _ := yaml.Marshal(nodeMap)
|
||||
retVal := wwapiv1.NodeYaml{
|
||||
NodeConfMapYaml: string(buffer),
|
||||
Hash: nodeDB.StringHash(),
|
||||
}
|
||||
return &retVal
|
||||
}
|
||||
|
||||
20
internal/pkg/api/node/hash.go
Normal file
20
internal/pkg/api/node/hash.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package apinode
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1"
|
||||
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
func Hash() *wwapiv1.NodeDBHash {
|
||||
config, err := node.New()
|
||||
if err != nil {
|
||||
wwlog.Warn("couldb't read config")
|
||||
}
|
||||
hash := config.Hash()
|
||||
return &wwapiv1.NodeDBHash{
|
||||
Hash: hex.EncodeToString(hash[:]),
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package apinode
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -29,7 +30,10 @@ func NodeAdd(nap *wwapiv1.NodeAddParameter) (err error) {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open node database")
|
||||
}
|
||||
|
||||
dbHash := nodeDB.Hash()
|
||||
if hex.EncodeToString(dbHash[:]) != nap.Hash && !nap.Force {
|
||||
return fmt.Errorf("got wrong hash, not modifying node database")
|
||||
}
|
||||
node_args := hostlist.Expand(nap.NodeNames)
|
||||
var nodeConf node.NodeConf
|
||||
err = yaml.Unmarshal([]byte(nap.NodeConfYaml), &nodeConf)
|
||||
@@ -95,6 +99,10 @@ func NodeDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) {
|
||||
wwlog.Error("Failed to open node database: %s", err)
|
||||
return
|
||||
}
|
||||
dbHash := nodeDB.Hash()
|
||||
if hex.EncodeToString(dbHash[:]) != ndp.Hash && !ndp.Force {
|
||||
return fmt.Errorf("got wrong hash, not modifying node database")
|
||||
}
|
||||
|
||||
for _, n := range nodeList {
|
||||
err := nodeDB.DelNode(n.Id.Get())
|
||||
@@ -133,6 +141,13 @@ func NodeDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (n
|
||||
wwlog.Error("Failed to open node database: %s", err)
|
||||
return
|
||||
}
|
||||
dbHash := nodeDB.Hash()
|
||||
if hex.EncodeToString(dbHash[:]) != ndp.Hash && !ndp.Force {
|
||||
wwlog.Debug("got hash: %s", ndp.Hash)
|
||||
wwlog.Debug("actual hash: %s", hex.EncodeToString(dbHash[:]))
|
||||
err = fmt.Errorf("got wrong hash, not modifying node database")
|
||||
return
|
||||
}
|
||||
|
||||
nodes, err := nodeDB.FindAllNodes()
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,12 @@ package wwapi.v1;
|
||||
import "google/protobuf/empty.proto";
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
// Information about the database
|
||||
|
||||
message NodeDBHash {
|
||||
string hash = 1;
|
||||
}
|
||||
|
||||
// Container
|
||||
|
||||
// ContainerBuildParameter contains input for building zero or more containers.
|
||||
@@ -133,9 +139,12 @@ message ProfileList {
|
||||
}
|
||||
|
||||
// NodeAddParameter contains all input for adding a node to be managed by
|
||||
// Warewulf.
|
||||
// Warewulf. Only adds nodes if the hash matches the actual hash of the
|
||||
// configuration.
|
||||
message NodeAddParameter {
|
||||
string nodeConfYaml = 1;
|
||||
bool force = 2;
|
||||
string hash = 3;
|
||||
repeated string nodeNames = 10;
|
||||
}
|
||||
|
||||
@@ -143,13 +152,17 @@ message NodeAddParameter {
|
||||
// to nodes.conf (is resused for profile edit)
|
||||
message NodeYaml {
|
||||
string nodeConfMapYaml = 1;
|
||||
string hash = 2;
|
||||
}
|
||||
|
||||
// NodeDeleteParameter contains input for removing nodes from Warewulf
|
||||
// management.
|
||||
// management. If the given hash differs with the actual hash of the
|
||||
// configuration, no node is deleted. The force option allows the deletion
|
||||
// of nodes with a correct hash.
|
||||
message NodeDeleteParameter {
|
||||
bool force = 1;
|
||||
repeated string nodeNames = 2;
|
||||
string hash = 3;
|
||||
}
|
||||
|
||||
// NodeSetParameter contains all fields for updating aspects of nodes managed
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package node
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/hpcng/warewulf/internal/pkg/wwlog"
|
||||
"gopkg.in/yaml.v2"
|
||||
@@ -24,3 +25,11 @@ func (config *NodeYaml) Hash() [32]byte {
|
||||
}
|
||||
return sha256.Sum256(data)
|
||||
}
|
||||
|
||||
/*
|
||||
Return the hash as string
|
||||
*/
|
||||
func (config *NodeYaml) StringHash() string {
|
||||
buffer := config.Hash()
|
||||
return hex.EncodeToString(buffer[:])
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package node
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
@@ -30,7 +29,7 @@ nodes:
|
||||
profiles:
|
||||
- default
|
||||
network devices:
|
||||
testnet:
|
||||
default:
|
||||
ipaddr: 10.0.10.2
|
||||
`
|
||||
nodeConfYml2 := `
|
||||
@@ -81,7 +80,6 @@ nodes:
|
||||
`
|
||||
var nodeConf1, nodeConf2, nodeConf3 NodeYaml
|
||||
err := yaml.Unmarshal([]byte(nodeConfYml1), &nodeConf1)
|
||||
fmt.Println(nodeConf1.Nodes["n01"])
|
||||
assert.NoError(t, err)
|
||||
err = yaml.Unmarshal([]byte(nodeConfYml2), &nodeConf2)
|
||||
assert.NoError(t, err)
|
||||
@@ -98,8 +96,12 @@ nodes:
|
||||
})
|
||||
|
||||
t.Run("Different sorted NodeYaml with same conf", func(t *testing.T) {
|
||||
yml1, err := yaml.Marshal(nodeConf1)
|
||||
assert.NoError(t, err)
|
||||
yml2, err := yaml.Marshal(nodeConf2)
|
||||
assert.NoError(t, err)
|
||||
if nodeConf2.Hash() != nodeConf1.Hash() {
|
||||
t.Errorf("Hashes for same configuration differs: %x != %x", nodeConf2.Hash(), nodeConf1.Hash())
|
||||
t.Errorf("Hashes for same configuration differs: %x != %x\njson1:\n%s,yml2:\n%s\n", nodeConf2.Hash(), nodeConf1.Hash(), yml1, yml2)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user