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

keystore commands for ox #588

Merged
merged 11 commits into from
Aug 29, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@
* `svc_flex_cpu_low_threshold`
* `svc_flex_cpu_high_threshold`

* **breaking change:** key-value stores (cfg, sec, usr kinded objects) `change` action is no longer failing if the key does not exist. The key is added instead.

### commands

* **breaking change:** "om node freeze" is now local only. Use "om cluster freeze" for the orchestrated freeze of all nodes. Same applies to "unfreeze" and its hidden alias "thaw".
Expand Down
7 changes: 7 additions & 0 deletions core/naming/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ var (
string(KindNscfg): nil,
}

KindKVStore = []Kind{
KindCfg,
KindSec,
KindUsr,
}

KindAll = []Kind{
KindSvc,
KindVol,
Expand All @@ -55,6 +61,7 @@ var (
KindCcfg,
KindNscfg,
}

KindStrings = []string{
string(KindSvc),
string(KindVol),
Expand Down
18 changes: 14 additions & 4 deletions core/object/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/opensvc/om3/util/plog"
)

var ErrWrongType = errors.New("wrong type provided for interface")

// WithConfigFile sets a non-standard configuration location.
func WithConfigFile(s string) funcopt.O {
return funcopt.F(func(t any) error {
Expand Down Expand Up @@ -87,34 +89,42 @@ func New(p naming.Path, opts ...funcopt.O) (any, error) {
func NewCore(p naming.Path, opts ...funcopt.O) (Core, error) {
if o, err := New(p, opts...); err != nil {
return nil, err
} else if i, ok := o.(Core); ok {
return i, nil
} else {
return o.(Core), nil
return nil, ErrWrongType
}
}

// NewConfigurer returns a Configurer interface from an object path
func NewConfigurer(p naming.Path, opts ...funcopt.O) (Configurer, error) {
if o, err := New(p, opts...); err != nil {
return nil, err
} else if i, ok := o.(Configurer); ok {
return i, nil
} else {
return o.(Configurer), nil
return nil, ErrWrongType
}
}

// NewActor returns a Actor interface from an object path
func NewActor(p naming.Path, opts ...funcopt.O) (Actor, error) {
if o, err := New(p, opts...); err != nil {
return nil, err
} else if i, ok := o.(Actor); ok {
return i, nil
} else {
return o.(Actor), nil
return nil, ErrWrongType
}
}

// NewKeystore returns a Keystore interface from an object path
func NewKeystore(p naming.Path, opts ...funcopt.O) (Keystore, error) {
if o, err := New(p, opts...); err != nil {
return nil, err
} else if i, ok := o.(Keystore); ok {
return i, nil
} else {
return o.(Keystore), nil
return nil, ErrWrongType
}
}
20 changes: 18 additions & 2 deletions core/object/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package object

import (
"os"
"path/filepath"

"github.com/opensvc/om3/util/key"
)
Expand All @@ -26,16 +27,18 @@ type (
Core
HasKey(name string) bool
AddKey(name string, b []byte) error
AddKeyFrom(name string, from string) error
ChangeKey(name string, b []byte) error
ChangeKeyFrom(name string, from string) error
DecodeKey(keyname string) ([]byte, error)
AllKeys() ([]string, error)
MatchingKeys(string) ([]string, error)
RemoveKey(name string) error
EditKey(name string) error
InstallKey(name string) error
InstallKeyTo(string, string, *os.FileMode, *os.FileMode, string, string) error

TransactionAddKey(name string, b []byte) error
TransactionChangeKey(name string, b []byte) error
TransactionRemoveKey(name string) error
}

// SecureKeystore is implemented by encrypting Keystore object kinds (usr, sec).
Expand Down Expand Up @@ -73,3 +76,16 @@ func (t *keystore) temporaryKeyFile(name string) (f *os.File, err error) {
func (t *keystore) postCommit() error {
return t.postInstall("")
}

func FileToKey(path, prefix string) (string, error) {
if path == "" {
return prefix, nil
}
path = filepath.Clean(path)
dirName := filepath.Dir(path)
relPath, err := filepath.Rel(dirName, path)
if prefix == "" {
return relPath, err
}
return filepath.Join(prefix, relPath), nil
}
118 changes: 20 additions & 98 deletions core/object/keystore_add.go
Original file line number Diff line number Diff line change
@@ -1,128 +1,50 @@
package object

import (
"bufio"
"fmt"
"io"
"os"
"errors"

"github.com/opensvc/om3/core/keyop"
"github.com/opensvc/om3/util/file"
"github.com/opensvc/om3/util/uri"
)

// AddKey sets a new key and commits immediately
func (t *keystore) AddKey(name string, b []byte) error {
if t.HasKey(name) {
return fmt.Errorf("key already exist: %s. use the change action", name)
}
if err := t.addKey(name, b); err != nil {
return err
}
return t.config.Commit()
}
var (
KeystoreErrExist = errors.New("key already exists")
KeystoreErrKeyEmpty = errors.New("key is empty")
KeystoreErrNotExist = errors.New("key does not exist")
)

// ChangeKey changes the value of a existing key and commits immediately
func (t *keystore) ChangeKey(name string, b []byte) error {
if !t.HasKey(name) {
return fmt.Errorf("key does not exist: %s. use the add action", name)
}
if err := t.addKey(name, b); err != nil {
return err
}
return t.config.Commit()
}
func (t *keystore) AddKeyFrom(name string, from string) error {
if name == "" {
return fmt.Errorf("key name can not be empty")
}
// TransactionAddKey sets a new key
func (t *keystore) TransactionAddKey(name string, b []byte) error {
if t.HasKey(name) {
return fmt.Errorf("key already exist: %s. use the change action", name)
}
if err := t.alterFrom(name, from); err != nil {
return err
return KeystoreErrExist
}
return t.config.Commit()
return t.addKey(name, b)
}

func (t *keystore) ChangeKeyFrom(name string, from string) error {
if name == "" {
return fmt.Errorf("key name can not be empty")
}
if !t.HasKey(name) {
return fmt.Errorf("key does not exist: %s. use the add action", name)
}
if err := t.alterFrom(name, from); err != nil {
// AddKey sets a new key and commits immediately
func (t *keystore) AddKey(name string, b []byte) error {
if err := t.TransactionAddKey(name, b); err != nil {
return err
}
return t.config.Commit()
}

func (t *keystore) alterFrom(name string, from string) error {
switch from {
case "":
return fmt.Errorf("empty value source")
case "-", "stdin", "/dev/stdin":
return t.fromStdin(name)
default:
u := uri.New(from)
if u.IsValid() {
return t.fromURI(name, u)
}
if v, err := file.ExistsAndRegular(from); err != nil {
return err
} else if v {
return t.fromRegular(name, from)
}
if v, err := file.ExistsAndDir(from); err != nil {
return err
} else if v {
return t.fromDir(name, from)
}
return fmt.Errorf("unexpected value source: %s", from)
}
}

func (t *keystore) fromStdin(name string) error {
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
reader := bufio.NewReader(os.Stdin)
b, err := io.ReadAll(reader)
if err != nil {
return err
}
return t.addKey(name, b)
} else {
return fmt.Errorf("stdin must be a pipe")
}
}

func (t *keystore) fromRegular(name string, p string) error {
b, err := os.ReadFile(p)
if err != nil {
return err
}
// TransactionChangeKey inserts or updates the value of a existing key
func (t *keystore) TransactionChangeKey(name string, b []byte) error {
return t.addKey(name, b)
}

func (t *keystore) fromDir(name string, p string) error {
// TODO: walk and call fromRegular
return nil
}

func (t *keystore) fromURI(name string, u uri.T) error {
fName, err := u.Fetch()
if err != nil {
// ChangeKey changes the value of a existing key and commits immediately
func (t *keystore) ChangeKey(name string, b []byte) error {
if err := t.TransactionChangeKey(name, b); err != nil {
return err
}
defer os.Remove(fName)
return t.fromRegular(name, fName)
return t.config.Commit()
}

// Note: addKey does not commit, so it can be used multiple times efficiently.
func (t *keystore) addKey(name string, b []byte) error {
if name == "" {
return fmt.Errorf("key name can not be empty")
return KeystoreErrKeyEmpty
}
if b == nil {
b = []byte{}
Expand Down
1 change: 0 additions & 1 deletion core/object/keystore_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ func (t *keystore) writeKey(vk vKey, dst string, b []byte, mode *os.FileMode, us
}
}
t.log.Infof("install %s/%s in %s", t.path.Name, vk.Key, dst)
fmt.Printf("install %s/%s in %s\n", t.path.Name, vk.Key, dst)
perm := os.ModePerm
if mode != nil {
perm = *mode
Expand Down
6 changes: 6 additions & 0 deletions core/object/keystore_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import (
)

// RemoveKey removes a keyword from object
func (t *keystore) TransactionRemoveKey(keyname string) error {
k := key.New(dataSectionName, keyname)
return t.config.PrepareUnset(k)
}

// RemoveKey removes a keyword from object and commits immediately
func (t *keystore) RemoveKey(keyname string) error {
k := key.New(dataSectionName, keyname)
return t.config.Unset(k)
Expand Down
20 changes: 16 additions & 4 deletions core/omcmd/keystore_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/opensvc/om3/core/naming"
"github.com/opensvc/om3/core/object"
"github.com/opensvc/om3/core/objectaction"
"github.com/opensvc/om3/util/uri"
)

type (
Expand All @@ -31,12 +32,23 @@ func (t *CmdKeystoreAdd) Run(selector, kind string) error {
if err != nil {
return nil, err
}
switch {
case t.From != "":
return nil, store.AddKeyFrom(t.Key, t.From)
default:
if t.Value != "" {
return nil, store.AddKey(t.Key, []byte(t.Value))
}
m, err := uri.ReadAllFrom(t.From)
if err != nil {
return nil, err
}
for path, b := range m {
k, err := object.FileToKey(path, t.Key)
if err != nil {
return nil, err
}
if err := store.TransactionAddKey(k, b); err != nil {
return nil, err
}
}
return nil, store.Config().CommitInvalid()
}),
).Do()
}
20 changes: 16 additions & 4 deletions core/omcmd/keystore_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/opensvc/om3/core/naming"
"github.com/opensvc/om3/core/object"
"github.com/opensvc/om3/core/objectaction"
"github.com/opensvc/om3/util/uri"
)

type (
Expand All @@ -30,13 +31,24 @@ func (t *CmdKeystoreChange) Run(selector, kind string) error {
if err != nil {
return nil, err
}
switch {
case t.From != "":
return nil, store.ChangeKeyFrom(t.Key, t.From)
default:
if t.Value != "" {
return nil, store.ChangeKey(t.Key, []byte(t.Value))
}
m, err := uri.ReadAllFrom(t.From)
if err != nil {
return nil, err
}
for path, b := range m {
k, err := object.FileToKey(path, t.Key)
if err != nil {
return nil, err
}

if err := store.TransactionChangeKey(k, b); err != nil {
return nil, err
}
}
return nil, store.Config().CommitInvalid()
}),
).Do()
}
6 changes: 6 additions & 0 deletions core/ox/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func init() {
cmdObjectSet,
cmdObjectSync,
cmdObjectValidate,
newCmdKeystoreAdd(kind),
newCmdKeystoreChange(kind),
newCmdKeystoreDecode(kind),
newCmdKeystoreKeys(kind),
newCmdKeystoreInstall(kind),
newCmdKeystoreRemove(kind),
newCmdObjectAbort(kind),
newCmdObjectBoot(kind),
newCmdObjectClear(kind),
Expand Down
Loading
Loading