diff --git a/rados/snapshot.go b/rados/snapshot.go index 3dfd201..db4d145 100644 --- a/rados/snapshot.go +++ b/rados/snapshot.go @@ -168,3 +168,22 @@ func (ioctx *IOContext) RollbackSnap(oid, snapName string) error { ret := C.rados_ioctx_snap_rollback(ioctx.ioctx, coid, cSnapName) return getError(ret) } + +// SnapHead is the representation of LIBRADOS_SNAP_HEAD from librados. +// SnapHead can be used to reset the IOContext to stop reading from a snapshot. +const SnapHead = SnapID(C.LIBRADOS_SNAP_HEAD) + +// SetReadSnap sets the snapshot from which reads are performed. +// Subsequent reads will return data as it was at the time of that snapshot. +// Pass SnapHead for no snapshot (i.e. normal operation). +// +// Implements: +// void rados_ioctx_snap_set_read(rados_ioctx_t io, rados_snap_t snap); +func (ioctx *IOContext) SetReadSnap(snapID SnapID) error { + if err := ioctx.validate(); err != nil { + return err + } + + C.rados_ioctx_snap_set_read(ioctx.ioctx, (C.rados_snap_t)(snapID)) + return nil +} diff --git a/rados/snapshot_test.go b/rados/snapshot_test.go index bed3c23..4c73786 100644 --- a/rados/snapshot_test.go +++ b/rados/snapshot_test.go @@ -254,3 +254,59 @@ func (suite *RadosTestSuite) TestRollbackSnapshot() { assert.Equal(t, bytesOut, bytesIn) }) } + +func (suite *RadosTestSuite) TestSetReadSnapshot() { + suite.SetupConnection() + ioctx, err := suite.conn.OpenIOContext(suite.pool) + require.NoError(suite.T(), err) + + bytesIn := []byte("The Order of the Phoenix") + err = ioctx.Write("obj", bytesIn, 0) + assert.NoError(suite.T(), err) + + // Take snap. + err = ioctx.CreateSnap("mySnap") + assert.NoError(suite.T(), err) + defer func() { + assert.NoError(suite.T(), ioctx.RemoveSnap("mySnap")) + }() + + // Get Snap ID. + snapID, err := ioctx.LookupSnap("mySnap") + assert.NoError(suite.T(), err) + assert.NotNil(suite.T(), snapID) + + // Overwrite the object. + bytesOver := []byte("The Half blood Prince") + err = ioctx.Write("obj", bytesOver, 0) + assert.NoError(suite.T(), err) + + // Set read to mySnap. + err = ioctx.SetReadSnap(snapID) + assert.NoError(suite.T(), err) + + // Read the object. + bytesOut := make([]byte, len(bytesIn)) + nOut, err := ioctx.Read("obj", bytesOut, 0) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), nOut, len(bytesIn)) + assert.Equal(suite.T(), bytesOut, bytesIn) + + // Set read to SnapHead (back to normal). + err = ioctx.SetReadSnap(SnapHead) + assert.NoError(suite.T(), err) + + // Read the same object. + bytesOut = make([]byte, len(bytesOver)) + nOut, err = ioctx.Read("obj", bytesOut, 0) + assert.NoError(suite.T(), err) + assert.Equal(suite.T(), nOut, len(bytesOver)) + assert.Equal(suite.T(), bytesOut, bytesOver) + + suite.T().Run("invalidIOContext", func(t *testing.T) { + ioctx := &IOContext{} + err := ioctx.SetReadSnap(SnapHead) + assert.Error(t, err) + assert.Equal(t, err, ErrInvalidIOContext) + }) +}