-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial implementation of the WAL service
Signed-off-by: Leonardo Cecchi <[email protected]>
- Loading branch information
1 parent
3f942ff
commit 33c27ee
Showing
12 changed files
with
411 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
cloudnative-pg | ||
bin | ||
.github | ||
.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package fileutils | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"os" | ||
"os/user" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
// This implementation is based on https://github.com/nmrshll/go-cp/blob/master/cp.go | ||
|
||
func replaceHomeFolder(path string) (string, error) { | ||
if !strings.HasPrefix(path, "~") { | ||
return path, nil | ||
} | ||
var buffer bytes.Buffer | ||
usr, err := user.Current() | ||
if err != nil { | ||
return "", err | ||
} | ||
_, err = buffer.WriteString(usr.HomeDir) | ||
if err != nil { | ||
return "", err | ||
} | ||
_, err = buffer.WriteString(strings.TrimPrefix(path, "~")) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return buffer.String(), nil | ||
} | ||
|
||
// AbsolutePath converts a path (relative or absolute) into an absolute one. | ||
// Supports '~' notation for $HOME directory of the current user. | ||
func AbsolutePath(path string) (string, error) { | ||
homeReplaced, err := replaceHomeFolder(path) | ||
if err != nil { | ||
return "", err | ||
} | ||
return filepath.Abs(homeReplaced) | ||
} | ||
|
||
// CopyFile copies a file from src to dst. If src and dst files exist, and are | ||
// the same, then return success. Otherwise, attempt to create a hard link | ||
// between the two files. If that fails, copy the file contents from src to dst. | ||
// Creates any missing directories. Supports '~' notation for $HOME directory of the current user. | ||
func CopyFile(src, dst string) error { | ||
srcAbs, err := AbsolutePath(src) | ||
if err != nil { | ||
return err | ||
} | ||
dstAbs, err := AbsolutePath(dst) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// open source file | ||
sfi, err := os.Stat(srcAbs) | ||
if err != nil { | ||
return err | ||
} | ||
if !sfi.Mode().IsRegular() { | ||
// cannot copy non-regular files (e.g., directories, | ||
// symlinks, devices, etc.) | ||
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String()) | ||
} | ||
|
||
// open dest file | ||
dfi, err := os.Stat(dstAbs) | ||
if err != nil && !os.IsNotExist(err) { | ||
return err | ||
} | ||
|
||
if err != nil { | ||
// file doesn't exist | ||
err := os.MkdirAll(filepath.Dir(dst), 0o750) | ||
if err != nil { | ||
return err | ||
} | ||
} else { | ||
if !(dfi.Mode().IsRegular()) { | ||
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) | ||
} | ||
if os.SameFile(sfi, dfi) { | ||
return err | ||
} | ||
} | ||
if err = os.Link(src, dst); err == nil { | ||
return err | ||
} | ||
return copyFileContents(src, dst) | ||
} | ||
|
||
// copyFileContents copies the contents of the file named src to the file named | ||
// by dst. The file will be created if it does not already exist. If the | ||
// destination file exists, all it's contents will be replaced by the contents | ||
// of the source file. | ||
func copyFileContents(src, dst string) error { | ||
// Open the source file for reading | ||
srcFile, err := os.Open(src) // nolint:gosec | ||
if err != nil { | ||
return err | ||
} | ||
defer func() { | ||
_ = srcFile.Close() | ||
}() | ||
|
||
// Open the destination file for writing | ||
dstFile, err := os.Create(dst) // nolint:gosec | ||
if err != nil { | ||
return err | ||
} | ||
// Return any errors that result from closing the destination file | ||
// Will return nil if no errors occurred | ||
defer func() { | ||
cerr := dstFile.Close() | ||
if err == nil { | ||
err = cerr | ||
} | ||
}() | ||
|
||
// Copy the contents of the source file into the destination files | ||
size := 1024 * 1024 | ||
buf := make([]byte, size) | ||
if _, err = io.CopyBuffer(dstFile, srcFile, buf); err != nil { | ||
return err | ||
} | ||
err = dstFile.Sync() | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package fileutils contains a set of useful functions to manage files | ||
package fileutils |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package wal contains the implementation of the | ||
// WAL Manager server | ||
package wal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package wal | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/cloudnative-pg/cnpg-i/pkg/wal" | ||
) | ||
|
||
// Implementation is the implementation of the identity service | ||
type Implementation struct { | ||
wal.WALServer | ||
} | ||
|
||
// GetCapabilities gets the capabilities of the WAL service | ||
func (Implementation) GetCapabilities( | ||
context.Context, | ||
*wal.WALCapabilitiesRequest, | ||
) (*wal.WALCapabilitiesResult, error) { | ||
return &wal.WALCapabilitiesResult{ | ||
Capabilities: []*wal.WALCapability{ | ||
{ | ||
Type: &wal.WALCapability_Rpc{ | ||
Rpc: &wal.WALCapability_RPC{ | ||
Type: wal.WALCapability_RPC_TYPE_ARCHIVE_WAL, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Type: &wal.WALCapability_Rpc{ | ||
Rpc: &wal.WALCapability_RPC{ | ||
Type: wal.WALCapability_RPC_TYPE_RESTORE_WAL, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Type: &wal.WALCapability_Rpc{ | ||
Rpc: &wal.WALCapability_RPC{ | ||
Type: wal.WALCapability_RPC_TYPE_STATUS, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Type: &wal.WALCapability_Rpc{ | ||
Rpc: &wal.WALCapability_RPC{ | ||
Type: wal.WALCapability_RPC_TYPE_SET_FIRST_REQUIRED, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package wal | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io/fs" | ||
"os" | ||
"path" | ||
|
||
"github.com/cloudnative-pg/cnpg-i/pkg/wal" | ||
|
||
"github.com/cloudnative-pg/plugin-pvc-backup/pkg/logging" | ||
"github.com/cloudnative-pg/plugin-pvc-backup/pkg/metadata" | ||
"github.com/cloudnative-pg/plugin-pvc-backup/pkg/pluginhelper" | ||
) | ||
|
||
type walStatMode string | ||
|
||
const ( | ||
walStatModeFirst = "first" | ||
walStatModeLast = "last" | ||
) | ||
|
||
// Status gets the statistics of the WAL file archive | ||
func (Implementation) Status( | ||
ctx context.Context, | ||
request *wal.WALStatusRequest, | ||
) (*wal.WALStatusResult, error) { | ||
logging := logging.FromContext(ctx) | ||
|
||
helper, err := pluginhelper.NewFromCluster(metadata.Data.Name, request.ClusterDefinition) | ||
if err != nil { | ||
logging.Error(err, "Error while decoding cluster definition from CNPG") | ||
return nil, err | ||
} | ||
|
||
walPath := getWALPath(helper.GetCluster().Name) | ||
logging = logging.WithValues( | ||
"walPath", walPath, | ||
"clusterName", helper.GetCluster().Name, | ||
) | ||
|
||
walDirEntries, err := os.ReadDir(walPath) | ||
if err != nil { | ||
logging.Error(err, "Error while reading WALs directory") | ||
return nil, err | ||
} | ||
|
||
firstWal, err := getWALStat(helper.GetCluster().Name, walDirEntries, walStatModeFirst) | ||
if err != nil { | ||
logging.Error(err, "Error while reading WALs directory (getting first WAL)") | ||
return nil, err | ||
} | ||
|
||
lastWal, err := getWALStat(helper.GetCluster().Name, walDirEntries, walStatModeLast) | ||
if err != nil { | ||
logging.Error(err, "Error while reading WALs directory (getting first WAL)") | ||
return nil, err | ||
} | ||
|
||
return &wal.WALStatusResult{ | ||
FirstWal: firstWal, | ||
LastWal: lastWal, | ||
}, nil | ||
} | ||
|
||
func getWALStat(clusterName string, entries []fs.DirEntry, mode walStatMode) (string, error) { | ||
entry, ok := getEntry(entries, mode) | ||
if !ok { | ||
return "", nil | ||
} | ||
|
||
if !entry.IsDir() { | ||
return "", fmt.Errorf("%s is not a directory", entry) | ||
} | ||
|
||
entryAbsolutePath := path.Join(getWALPath(clusterName), entry.Name()) | ||
subFolderEntries, err := os.ReadDir(entryAbsolutePath) | ||
if err != nil { | ||
return "", fmt.Errorf("while reading %s entries: %w", entry, err) | ||
} | ||
|
||
selectSubFolderEntry, ok := getEntry(subFolderEntries, mode) | ||
if !ok { | ||
return "", nil | ||
} | ||
|
||
return selectSubFolderEntry.Name(), nil | ||
} | ||
|
||
func getEntry(entries []fs.DirEntry, mode walStatMode) (fs.DirEntry, bool) { | ||
if len(entries) == 0 { | ||
return nil, false | ||
} | ||
|
||
switch mode { | ||
case walStatModeFirst: | ||
return entries[0], true | ||
|
||
case walStatModeLast: | ||
return entries[len(entries)-1], true | ||
|
||
default: | ||
return nil, false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package wal | ||
|
||
import "path" | ||
|
||
func getWalPrefix(walName string) string { | ||
return walName[0:16] | ||
} | ||
|
||
func getClusterPath(clusterName string) string { | ||
return path.Join(basePath, clusterName) | ||
} | ||
|
||
func getWALPath(clusterName string) string { | ||
return path.Join( | ||
getClusterPath(clusterName), | ||
walsDirectory, | ||
) | ||
} | ||
|
||
func getWALFilePath(clusterName string, walName string) string { | ||
return path.Join( | ||
getWALPath(clusterName), | ||
getWalPrefix(walName), | ||
walName, | ||
) | ||
} |
Oops, something went wrong.