1 Star 0 Fork 1

mysnapcore / mysnapd

forked from tupelo-shen / mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
copy.go 4.35 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-08 15:12 . fix: overlord commit
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2020 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package state
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"gitee.com/mysnapcore/mysnapd/jsonutil"
"gitee.com/mysnapcore/mysnapd/osutil"
)
type checkpointOnlyBackend struct {
path string
}
func (b *checkpointOnlyBackend) Checkpoint(data []byte) error {
if err := os.MkdirAll(filepath.Dir(b.path), 0755); err != nil {
return err
}
return osutil.AtomicWriteFile(b.path, data, 0600, 0)
}
func (b *checkpointOnlyBackend) EnsureBefore(d time.Duration) {
panic("cannot use EnsureBefore in checkpointOnlyBackend")
}
// copyData will copy the given subkeys specifier from srcData to dstData.
//
// The subkeys is constructed from a dotted path like "user.auth". This copy
// helper is recursive and the pos parameter tells the function the current
// position of the copy.
func copyData(subkeys []string, pos int, srcData map[string]*json.RawMessage, dstData map[string]interface{}) error {
if pos < 0 || pos > len(subkeys) {
return fmt.Errorf("internal error: copyData used with an out-of-bounds position: %v not in [0:%v]", pos, len(subkeys))
}
raw, ok := srcData[subkeys[pos]]
if !ok {
return ErrNoState
}
if pos+1 == len(subkeys) {
dstData[subkeys[pos]] = raw
return nil
}
var srcDatam map[string]*json.RawMessage
if err := jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &srcDatam); err != nil {
return fmt.Errorf("cannot unmarshal state entry %q with value %q as a map while trying to copy over %q", strings.Join(subkeys[:pos+1], "."), *raw, strings.Join(subkeys, "."))
}
// no subkey entry -> create one
if _, ok := dstData[subkeys[pos]]; !ok {
dstData[subkeys[pos]] = make(map[string]interface{})
}
// and use existing data
var dstDatam map[string]interface{}
switch dstDataEntry := dstData[subkeys[pos]].(type) {
case map[string]interface{}:
dstDatam = dstDataEntry
case *json.RawMessage:
dstDatam = make(map[string]interface{})
if err := jsonutil.DecodeWithNumber(bytes.NewReader(*dstDataEntry), &dstDatam); err != nil {
return fmt.Errorf("internal error: cannot decode subkey %s (%q) for %v (%T)", subkeys[pos], strings.Join(subkeys, "."), dstData, dstDataEntry)
}
default:
return fmt.Errorf("internal error: cannot create subkey %s (%q) for %v (%T)", subkeys[pos], strings.Join(subkeys, "."), dstData, dstData[subkeys[pos]])
}
return copyData(subkeys, pos+1, srcDatam, dstDatam)
}
// CopyState takes a state from the srcStatePath and copies all
// dataEntries to the dstPath. Note that srcStatePath should never
// point to a state that is in use.
func CopyState(srcStatePath, dstStatePath string, dataEntries []string) error {
if osutil.FileExists(dstStatePath) {
// XXX: TOCTOU - look into moving this check into
// checkpointOnlyBackend. The issue is right now State
// will simply panic if Commit() returns an error
return fmt.Errorf("cannot copy state: %q already exists", dstStatePath)
}
if len(dataEntries) == 0 {
return fmt.Errorf("cannot copy state: must provide at least one data entry to copy")
}
f, err := os.Open(srcStatePath)
if err != nil {
return fmt.Errorf("cannot open state: %s", err)
}
defer f.Close()
// No need to lock/unlock the state here, srcState should not be
// in use at all.
srcState, err := ReadState(nil, f)
if err != nil {
return err
}
// copy relevant data
dstData := make(map[string]interface{})
for _, dataEntry := range dataEntries {
subkeys := strings.Split(dataEntry, ".")
if err := copyData(subkeys, 0, srcState.data, dstData); err != nil && !errors.Is(err, ErrNoState) {
return err
}
}
// write it out
dstState := New(&checkpointOnlyBackend{path: dstStatePath})
dstState.Lock()
defer dstState.Unlock()
for k, v := range dstData {
dstState.Set(k, v)
}
return nil
}
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助

53164aa7 5694891 3bd8fe86 5694891