You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by mi...@apache.org on 2023/04/23 07:34:07 UTC
[shardingsphere-on-cloud] branch main updated: perf(pitr): check status of agent servers before do backup (#318)
This is an automated email from the ASF dual-hosted git repository.
miaoliyao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shardingsphere-on-cloud.git
The following commit(s) were added to refs/heads/main by this push:
new 3e8aef9 perf(pitr): check status of agent servers before do backup (#318)
3e8aef9 is described below
commit 3e8aef993fb8f91b84f8ffe2eac5f606a7dcf11d
Author: Xu-Wentao <cu...@yahoo.com>
AuthorDate: Sun Apr 23 15:34:02 2023 +0800
perf(pitr): check status of agent servers before do backup (#318)
---
pitr/cli/go.mod | 5 ++-
pitr/cli/go.sum | 14 ++++---
pitr/cli/internal/cmd/backup.go | 44 ++++++++++++++++++++++
pitr/cli/internal/cmd/backup_test.go | 54 +++++++++++++++++++++++++++
pitr/cli/internal/pkg/agent-server.go | 18 +++++++++
pitr/cli/internal/pkg/mocks/agent-server.go | 44 ++++++++++++++--------
pitr/cli/internal/pkg/model/as_backup.go | 5 +++
pitr/cli/internal/pkg/shardingsphere-proxy.go | 5 ++-
8 files changed, 166 insertions(+), 23 deletions(-)
diff --git a/pitr/cli/go.mod b/pitr/cli/go.mod
index 14b51d0..8d5a5f2 100644
--- a/pitr/cli/go.mod
+++ b/pitr/cli/go.mod
@@ -5,10 +5,9 @@ go 1.20
require (
bou.ke/monkey v1.0.2
gitee.com/opengauss/openGauss-connector-go-pq v1.0.4
- github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/golang/mock v1.6.0
github.com/google/uuid v1.3.0
- github.com/jarcoal/httpmock v1.3.0
+ github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/onsi/ginkgo/v2 v2.9.1
github.com/onsi/gomega v1.27.3
github.com/spf13/cobra v1.6.1
@@ -24,7 +23,9 @@ require (
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/kr/pretty v0.3.0 // indirect
+ github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/stretchr/testify v1.8.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
diff --git a/pitr/cli/go.sum b/pitr/cli/go.sum
index 3855af1..c85685e 100644
--- a/pitr/cli/go.sum
+++ b/pitr/cli/go.sum
@@ -4,8 +4,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
gitee.com/opengauss/openGauss-connector-go-pq v1.0.4 h1:npfLM9/QpkmdK+XY9X2pcC2EX5gosyn/6dRDRd2sEJs=
gitee.com/opengauss/openGauss-connector-go-pq v1.0.4/go.mod h1:2UEp+ug6ls6C0pLfZgBn7VBzBntFUzxJuy+6FlQ7qyI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
-github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -54,8 +52,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
-github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
+github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw=
+github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -64,7 +62,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk=
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk=
@@ -72,9 +71,12 @@ github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557c
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
@@ -90,6 +92,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@@ -143,6 +146,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/pitr/cli/internal/cmd/backup.go b/pitr/cli/internal/cmd/backup.go
index 5681fd8..25956fb 100644
--- a/pitr/cli/internal/cmd/backup.go
+++ b/pitr/cli/internal/cmd/backup.go
@@ -19,6 +19,7 @@ package cmd
import (
"fmt"
+ "os"
"sync"
"time"
@@ -27,6 +28,7 @@ import (
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/xerr"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/logging"
"github.com/google/uuid"
+ "github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/sync/errgroup"
@@ -221,6 +223,12 @@ func execBackup(lsBackup *model.LsBackup) error {
dnCh := make(chan *model.DataNode, len(sNodes))
g := new(errgroup.Group)
+ logging.Info("Checking agent server status...")
+ available := checkAgentServerStatus(lsBackup)
+ if !available {
+ return xerr.NewCliErr("one or more agent server are not available.")
+ }
+
logging.Info("Starting send backup command to agent server...")
for _, node := range sNodes {
@@ -253,6 +261,42 @@ func execBackup(lsBackup *model.LsBackup) error {
return nil
}
+func checkAgentServerStatus(lsBackup *model.LsBackup) bool {
+
+ statusList := make([]*model.AgentServerStatus, 0)
+
+ // all agent server are available
+ available := true
+
+ for _, node := range lsBackup.SsBackup.StorageNodes {
+ n := node
+ agentHost := n.IP
+ if agentHost == "127.0.0.1" {
+ agentHost = Host
+ }
+ as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", agentHost, AgentPort))
+ if err := as.CheckStatus(); err != nil {
+ statusList = append(statusList, &model.AgentServerStatus{IP: n.IP, Status: "Unavailable"})
+ available = false
+ } else {
+ statusList = append(statusList, &model.AgentServerStatus{IP: n.IP, Status: "Available"})
+ }
+ }
+ t := table.NewWriter()
+ t.SetOutputMirror(os.Stdout)
+ t.SetTitle("Agent Server Status")
+ t.AppendHeader(table.Row{"#", "Agent Server IP", "Status"})
+
+ for i, s := range statusList {
+ t.AppendRow([]interface{}{i + 1, s.IP, s.Status})
+ t.AppendSeparator()
+ }
+
+ t.Render()
+
+ return available
+}
+
func _execBackup(as pkg.IAgentServer, node *model.StorageNode, dnCh chan *model.DataNode) error {
in := &model.BackupIn{
DBPort: node.Port,
diff --git a/pitr/cli/internal/cmd/backup_test.go b/pitr/cli/internal/cmd/backup_test.go
index 8b77830..1f56485 100644
--- a/pitr/cli/internal/cmd/backup_test.go
+++ b/pitr/cli/internal/cmd/backup_test.go
@@ -18,6 +18,7 @@
package cmd
import (
+ "context"
"errors"
"bou.ke/monkey"
@@ -25,6 +26,8 @@ import (
mock_pkg "github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/mocks"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/model"
"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/xerr"
+ "github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/httputils"
+ mock_httputils "github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/httputils/mocks"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -276,4 +279,55 @@ var _ = Describe("test backup mock", func() {
Expect(backup()).To(BeNil())
})
})
+
+ Context("test check agent server status", func() {
+ var mockCtrl *gomock.Controller
+ var mockIreq *mock_httputils.MockIreq
+
+ ls := &model.LsBackup{
+ Info: nil,
+ DnList: nil,
+ SsBackup: &model.SsBackup{
+ StorageNodes: []*model.StorageNode{
+ {
+ IP: "127.0.0.1",
+ Port: 3306,
+ },
+ {
+ IP: "127.0.0.2",
+ Port: 3307,
+ },
+ },
+ },
+ }
+ BeforeEach(func() {
+ mockCtrl = gomock.NewController(GinkgoT())
+ mockIreq = mock_httputils.NewMockIreq(mockCtrl)
+
+ monkey.Patch(httputils.NewRequest, func(ctx context.Context, method, url string) httputils.Ireq {
+ return mockIreq
+ })
+ })
+
+ AfterEach(func() {
+ monkey.UnpatchAll()
+ mockCtrl.Finish()
+ })
+
+ It("agent server is not running", func() {
+ mockIreq.EXPECT().Send(gomock.Any()).Return(-1, errors.New("error")).AnyTimes()
+ Expect(checkAgentServerStatus(ls)).To(BeFalse())
+ })
+
+ It("agent server are running", func() {
+ mockIreq.EXPECT().Send(gomock.Any()).Return(200, nil).AnyTimes()
+ Expect(checkAgentServerStatus(ls)).To(BeTrue())
+ })
+
+ It("one agent server is not running", func() {
+ mockIreq.EXPECT().Send(gomock.Any()).Return(500, nil)
+ mockIreq.EXPECT().Send(gomock.Any()).Return(200, nil).AnyTimes()
+ Expect(checkAgentServerStatus(ls)).To(BeFalse())
+ })
+ })
})
diff --git a/pitr/cli/internal/pkg/agent-server.go b/pitr/cli/internal/pkg/agent-server.go
index 393d14f..a823a93 100644
--- a/pitr/cli/internal/pkg/agent-server.go
+++ b/pitr/cli/internal/pkg/agent-server.go
@@ -38,12 +38,15 @@ type agentServer struct {
}
type IAgentServer interface {
+ CheckStatus() error
Backup(in *model.BackupIn) (string, error)
Restore(in *model.RestoreIn) error
ShowDetail(in *model.ShowDetailIn) (*model.BackupInfo, error)
ShowList(in *model.ShowListIn) ([]model.BackupInfo, error)
}
+var _ IAgentServer = (*agentServer)(nil)
+
func NewAgentServer(addr string) IAgentServer {
return &agentServer{
addr: addr,
@@ -55,6 +58,21 @@ func NewAgentServer(addr string) IAgentServer {
}
}
+// CheckStatus check agent server is alive
+func (as *agentServer) CheckStatus() error {
+ url := fmt.Sprintf("%s/%s", as.addr, "ping")
+ r := httputils.NewRequest(context.Background(), http.MethodGet, url)
+ httpCode, err := r.Send(nil)
+ if err != nil {
+ efmt := "httputils.NewRequest[url=%s] return err=%s,wrap=%w"
+ return fmt.Errorf(efmt, url, err, xerr.NewCliErr(xerr.Unknown))
+ }
+ if httpCode != http.StatusOK {
+ return fmt.Errorf("httpCode=%d", httpCode)
+ }
+ return nil
+}
+
func (as *agentServer) Backup(in *model.BackupIn) (string, error) {
url := fmt.Sprintf("%s%s", as.addr, as._apiBackup)
diff --git a/pitr/cli/internal/pkg/mocks/agent-server.go b/pitr/cli/internal/pkg/mocks/agent-server.go
index e5cbb95..e2fc4c1 100644
--- a/pitr/cli/internal/pkg/mocks/agent-server.go
+++ b/pitr/cli/internal/pkg/mocks/agent-server.go
@@ -1,22 +1,22 @@
/*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements. See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You under the Apache License, Version 2.0
-* (the "License"); you may not use this file except in compliance with
-* the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
// Code generated by MockGen. DO NOT EDIT.
-// Source: internal/pkg/agent-server.go
+// Source: agent-server.go
// Package mock_pkg is a generated GoMock package.
package mock_pkg
@@ -66,6 +66,20 @@ func (mr *MockIAgentServerMockRecorder) Backup(in interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Backup", reflect.TypeOf((*MockIAgentServer)(nil).Backup), in)
}
+// CheckStatus mocks base method.
+func (m *MockIAgentServer) CheckStatus() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "CheckStatus")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// CheckStatus indicates an expected call of CheckStatus.
+func (mr *MockIAgentServerMockRecorder) CheckStatus() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckStatus", reflect.TypeOf((*MockIAgentServer)(nil).CheckStatus))
+}
+
// Restore mocks base method.
func (m *MockIAgentServer) Restore(in *model.RestoreIn) error {
m.ctrl.T.Helper()
diff --git a/pitr/cli/internal/pkg/model/as_backup.go b/pitr/cli/internal/pkg/model/as_backup.go
index 63c45ef..bd18148 100644
--- a/pitr/cli/internal/pkg/model/as_backup.go
+++ b/pitr/cli/internal/pkg/model/as_backup.go
@@ -40,3 +40,8 @@ type (
Data BackupOut `json:"data"`
}
)
+
+type AgentServerStatus struct {
+ IP string `json:"ip"`
+ Status string `json:"status"`
+}
diff --git a/pitr/cli/internal/pkg/shardingsphere-proxy.go b/pitr/cli/internal/pkg/shardingsphere-proxy.go
index 5fa9f2a..d66a4de 100644
--- a/pitr/cli/internal/pkg/shardingsphere-proxy.go
+++ b/pitr/cli/internal/pkg/shardingsphere-proxy.go
@@ -126,7 +126,10 @@ func (ss *shardingSphereProxy) ExportMetaData() (*model.ClusterInfo, error) {
return nil, fmt.Errorf("json unmarshal return err=%s", err)
}
- out.SnapshotInfo = nil
+ if out.SnapshotInfo != nil && out.SnapshotInfo.Csn == "" {
+ out.SnapshotInfo = nil
+ }
+
return &out, nil
}