datamanager: fix linter errors
Fix errors reported by default golangci-lint linters
This commit is contained in:
parent
ced6108963
commit
49d0238a1e
|
@ -72,28 +72,11 @@ func (c *WalChanges) putRevision(revision int64) {
|
||||||
c.revision = revision
|
c.revision = revision
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WalChanges) curWalSeq() string {
|
|
||||||
return c.walSeq
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WalChanges) getPut(dataType, id string) (string, bool) {
|
func (c *WalChanges) getPut(dataType, id string) (string, bool) {
|
||||||
walseq, ok := c.puts[dataType][id]
|
walseq, ok := c.puts[dataType][id]
|
||||||
return walseq, ok
|
return walseq, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WalChanges) getDeletesMap() map[string]struct{} {
|
|
||||||
dmap := map[string]struct{}{}
|
|
||||||
for p := range c.deletes {
|
|
||||||
dmap[p] = struct{}{}
|
|
||||||
}
|
|
||||||
return dmap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WalChanges) getDelete(p string) bool {
|
|
||||||
_, ok := c.deletes[p]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WalChanges) addPut(dataType, id, walseq string, revision int64) {
|
func (c *WalChanges) addPut(dataType, id, walseq string, revision int64) {
|
||||||
delete(c.deletes[dataType], id)
|
delete(c.deletes[dataType], id)
|
||||||
c.puts[dataType][id] = walseq
|
c.puts[dataType][id] = walseq
|
||||||
|
@ -188,7 +171,7 @@ func (d *DataManager) applyWalChangesAction(ctx context.Context, action *Action,
|
||||||
d.changes.actions[walSequence] = append(d.changes.actions[walSequence], action)
|
d.changes.actions[walSequence] = append(d.changes.actions[walSequence], action)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataManager) watcherLoop(ctx context.Context) error {
|
func (d *DataManager) watcherLoop(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
initialized := d.changes.initialized
|
initialized := d.changes.initialized
|
||||||
if !initialized {
|
if !initialized {
|
||||||
|
@ -204,7 +187,7 @@ func (d *DataManager) watcherLoop(ctx context.Context) error {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
d.log.Infof("watcher exiting")
|
d.log.Infof("watcher exiting")
|
||||||
return nil
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,9 +166,8 @@ func (d *DataManager) Run(ctx context.Context, readyCh chan struct{}) error {
|
||||||
go d.compactChangeGroupsLoop(ctx)
|
go d.compactChangeGroupsLoop(ctx)
|
||||||
go d.etcdPingerLoop(ctx)
|
go d.etcdPingerLoop(ctx)
|
||||||
|
|
||||||
select {
|
<-ctx.Done()
|
||||||
case <-ctx.Done():
|
d.log.Infof("walmanager exiting")
|
||||||
d.log.Infof("walmanager exiting")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ import (
|
||||||
|
|
||||||
var level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
var level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
|
||||||
var logger = slog.New(level)
|
var logger = slog.New(level)
|
||||||
var log = logger.Sugar()
|
|
||||||
|
|
||||||
func setupEtcd(t *testing.T, dir string) *testutil.TestEmbeddedEtcd {
|
func setupEtcd(t *testing.T, dir string) *testutil.TestEmbeddedEtcd {
|
||||||
tetcd, err := testutil.NewTestEmbeddedEtcd(t, logger, dir)
|
tetcd, err := testutil.NewTestEmbeddedEtcd(t, logger, dir)
|
||||||
|
@ -56,17 +55,10 @@ func setupEtcd(t *testing.T, dir string) *testutil.TestEmbeddedEtcd {
|
||||||
|
|
||||||
func shutdownEtcd(tetcd *testutil.TestEmbeddedEtcd) {
|
func shutdownEtcd(tetcd *testutil.TestEmbeddedEtcd) {
|
||||||
if tetcd.Etcd != nil {
|
if tetcd.Etcd != nil {
|
||||||
tetcd.Kill()
|
_ = tetcd.Kill()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopCheckpointer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *noopCheckpointer) Checkpoint(ctx context.Context, action *Action) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEtcdReset(t *testing.T) {
|
func TestEtcdReset(t *testing.T) {
|
||||||
dir, err := ioutil.TempDir("", "agola")
|
dir, err := ioutil.TempDir("", "agola")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,12 +67,18 @@ func TestEtcdReset(t *testing.T) {
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
tetcd := setupEtcd(t, etcdDir)
|
tetcd := setupEtcd(t, etcdDir)
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
ostDir, err := ioutil.TempDir(dir, "ost")
|
ostDir, err := ioutil.TempDir(dir, "ost")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ost, err := posix.New(ostDir)
|
ost, err := posix.New(ostDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -95,10 +93,13 @@ func TestEtcdReset(t *testing.T) {
|
||||||
DataTypes: []string{"datatype01"},
|
DataTypes: []string{"datatype01"},
|
||||||
}
|
}
|
||||||
dm, err := NewDataManager(ctx, logger, dmConfig)
|
dm, err := NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
dmReadyCh := make(chan struct{})
|
dmReadyCh := make(chan struct{})
|
||||||
|
|
||||||
t.Logf("starting datamanager")
|
t.Logf("starting datamanager")
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
actions := []*Action{
|
actions := []*Action{
|
||||||
|
@ -109,10 +110,8 @@ func TestEtcdReset(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedObjects := []string{}
|
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
objectID := fmt.Sprintf("object%02d", i)
|
objectID := fmt.Sprintf("object%02d", i)
|
||||||
expectedObjects = append(expectedObjects, objectID)
|
|
||||||
actions[0].ID = objectID
|
actions[0].ID = objectID
|
||||||
if _, err := dm.WriteWal(ctx, actions, nil); err != nil {
|
if _, err := dm.WriteWal(ctx, actions, nil); err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
@ -128,7 +127,9 @@ func TestEtcdReset(t *testing.T) {
|
||||||
t.Logf("stopping etcd")
|
t.Logf("stopping etcd")
|
||||||
// Reset etcd
|
// Reset etcd
|
||||||
shutdownEtcd(tetcd)
|
shutdownEtcd(tetcd)
|
||||||
tetcd.WaitDown(10 * time.Second)
|
if err := tetcd.WaitDown(10 * time.Second); err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
t.Logf("resetting etcd")
|
t.Logf("resetting etcd")
|
||||||
os.RemoveAll(etcdDir)
|
os.RemoveAll(etcdDir)
|
||||||
t.Logf("starting etcd")
|
t.Logf("starting etcd")
|
||||||
|
@ -149,10 +150,13 @@ func TestEtcdReset(t *testing.T) {
|
||||||
DataTypes: []string{"datatype01"},
|
DataTypes: []string{"datatype01"},
|
||||||
}
|
}
|
||||||
dm, err = NewDataManager(ctx, logger, dmConfig)
|
dm, err = NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
dmReadyCh = make(chan struct{})
|
dmReadyCh = make(chan struct{})
|
||||||
|
|
||||||
t.Logf("starting datamanager")
|
t.Logf("starting datamanager")
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -174,12 +178,18 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
tetcd := setupEtcd(t, etcdDir)
|
tetcd := setupEtcd(t, etcdDir)
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ostDir, err := ioutil.TempDir(dir, "ost")
|
ostDir, err := ioutil.TempDir(dir, "ost")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ost, err := posix.New(ostDir)
|
ost, err := posix.New(ostDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -193,6 +203,9 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
DataTypes: []string{"datatype01"},
|
DataTypes: []string{"datatype01"},
|
||||||
}
|
}
|
||||||
dm, err := NewDataManager(ctx, logger, dmConfig)
|
dm, err := NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actions := []*Action{
|
actions := []*Action{
|
||||||
{
|
{
|
||||||
|
@ -204,7 +217,7 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dmReadyCh := make(chan struct{})
|
dmReadyCh := make(chan struct{})
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -236,7 +249,7 @@ func TestConcurrentUpdate(t *testing.T) {
|
||||||
|
|
||||||
oldcgt = cgt
|
oldcgt = cgt
|
||||||
// this must work successfully
|
// this must work successfully
|
||||||
cgt, err = dm.WriteWal(ctx, actions, cgt)
|
_, err = dm.WriteWal(ctx, actions, cgt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -256,12 +269,18 @@ func TestWalCleaner(t *testing.T) {
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
tetcd := setupEtcd(t, etcdDir)
|
tetcd := setupEtcd(t, etcdDir)
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ostDir, err := ioutil.TempDir(dir, "ost")
|
ostDir, err := ioutil.TempDir(dir, "ost")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ost, err := posix.New(ostDir)
|
ost, err := posix.New(ostDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -277,6 +296,9 @@ func TestWalCleaner(t *testing.T) {
|
||||||
MinCheckpointWalsNum: 1,
|
MinCheckpointWalsNum: 1,
|
||||||
}
|
}
|
||||||
dm, err := NewDataManager(ctx, logger, dmConfig)
|
dm, err := NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
actions := []*Action{
|
actions := []*Action{
|
||||||
{
|
{
|
||||||
|
@ -288,7 +310,7 @@ func TestWalCleaner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dmReadyCh := make(chan struct{})
|
dmReadyCh := make(chan struct{})
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
|
@ -321,12 +343,18 @@ func TestReadObject(t *testing.T) {
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
tetcd := setupEtcd(t, etcdDir)
|
tetcd := setupEtcd(t, etcdDir)
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ostDir, err := ioutil.TempDir(dir, "ost")
|
ostDir, err := ioutil.TempDir(dir, "ost")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
ost, err := posix.New(ostDir)
|
ost, err := posix.New(ostDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
@ -340,9 +368,12 @@ func TestReadObject(t *testing.T) {
|
||||||
DataTypes: []string{"datatype01"},
|
DataTypes: []string{"datatype01"},
|
||||||
}
|
}
|
||||||
dm, err := NewDataManager(ctx, logger, dmConfig)
|
dm, err := NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
dmReadyCh := make(chan struct{})
|
dmReadyCh := make(chan struct{})
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -479,12 +510,18 @@ func TestCheckpoint(t *testing.T) {
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
etcdDir, err := ioutil.TempDir(dir, "etcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
tetcd := setupEtcd(t, etcdDir)
|
tetcd := setupEtcd(t, etcdDir)
|
||||||
defer shutdownEtcd(tetcd)
|
defer shutdownEtcd(tetcd)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
ostDir, err := ioutil.TempDir(dir, "ost")
|
ostDir, err := ioutil.TempDir(dir, "ost")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
ost, err := posix.New(ostDir)
|
ost, err := posix.New(ostDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
@ -502,8 +539,11 @@ func TestCheckpoint(t *testing.T) {
|
||||||
MaxDataFileSize: 10 * 1024,
|
MaxDataFileSize: 10 * 1024,
|
||||||
}
|
}
|
||||||
dm, err := NewDataManager(ctx, logger, dmConfig)
|
dm, err := NewDataManager(ctx, logger, dmConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
dmReadyCh := make(chan struct{})
|
dmReadyCh := make(chan struct{})
|
||||||
go dm.Run(ctx, dmReadyCh)
|
go func() { _ = dm.Run(ctx, dmReadyCh) }()
|
||||||
<-dmReadyCh
|
<-dmReadyCh
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
@ -667,7 +707,7 @@ func TestCheckpoint(t *testing.T) {
|
||||||
}
|
}
|
||||||
actionGroups = append(actionGroups, actions)
|
actionGroups = append(actionGroups, actions)
|
||||||
|
|
||||||
currentEntries, err = testCheckpoint(t, ctx, dm, actionGroups, currentEntries)
|
_, err = testCheckpoint(t, ctx, dm, actionGroups, currentEntries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("unexpected err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,24 +143,6 @@ func (d *DataManager) ReadObject(dataType, id string, cgNames []string) (io.Read
|
||||||
return ioutil.NopCloser(f), cgt, err
|
return ioutil.NopCloser(f), cgt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataManager) changesList(paths []string, prefix, startWith string, recursive bool) []string {
|
|
||||||
fpaths := []string{}
|
|
||||||
for _, p := range paths {
|
|
||||||
if !recursive && len(p) > len(prefix) {
|
|
||||||
rel := strings.TrimPrefix(p, prefix)
|
|
||||||
skip := strings.Contains(rel, d.ost.Delimiter())
|
|
||||||
if skip {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(p, prefix) && p > startWith {
|
|
||||||
fpaths = append(fpaths, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fpaths
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataManager) HasOSTWal(walseq string) (bool, error) {
|
func (d *DataManager) HasOSTWal(walseq string) (bool, error) {
|
||||||
_, err := d.ost.Stat(d.storageWalStatusFile(walseq) + ".committed")
|
_, err := d.ost.Stat(d.storageWalStatusFile(walseq) + ".committed")
|
||||||
if err == ostypes.ErrNotExist {
|
if err == ostypes.ErrNotExist {
|
||||||
|
@ -601,7 +583,7 @@ func (d *DataManager) sync(ctx context.Context) error {
|
||||||
if err := m.Lock(ctx); err != nil {
|
if err := m.Lock(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer m.Unlock(ctx)
|
defer func() { _ = m.Unlock(ctx) }()
|
||||||
|
|
||||||
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -695,7 +677,7 @@ func (d *DataManager) checkpoint(ctx context.Context) error {
|
||||||
if err := m.Lock(ctx); err != nil {
|
if err := m.Lock(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer m.Unlock(ctx)
|
defer func() { _ = m.Unlock(ctx) }()
|
||||||
|
|
||||||
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -774,7 +756,7 @@ func (d *DataManager) walCleaner(ctx context.Context) error {
|
||||||
if err := m.Lock(ctx); err != nil {
|
if err := m.Lock(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer m.Unlock(ctx)
|
defer func() { _ = m.Unlock(ctx) }()
|
||||||
|
|
||||||
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
resp, err := d.e.List(ctx, etcdWalsDir+"/", "", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue