f1b8d88d6b
* Improving mattermost auth implementation * Making mattermost-auth based on shared database access * Reverting unneeded changes in the config.json file * Fixing tiny problems * Removing the need of using the mattermost session token * Fixing some bugs and allowing to not-bind the server to any port * Small fix to correctly get the templates * Adding the mattermost-plugin code inside focalboard repo * Adding a not working code part of the cluster websocket communication * Updating the mattermost version * Adding the cluster messages for the websockets * Updating to the new node version * Making it compatible with S3 * Addressing some tiny problems * Fixing server tests * Adds support for MySQL migrations and initialization Co-authored-by: Miguel de la Cruz <miguel@mcrx.me>
214 lines
5.7 KiB
Go
214 lines
5.7 KiB
Go
package plan
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ActionConditions adds condition support to actions.
|
|
type ActionConditions struct {
|
|
// Conditions are checkers run before executing the
|
|
// action. If any one fails (returns an error), the action
|
|
// itself is not executed.
|
|
Conditions []Check
|
|
}
|
|
|
|
// Check runs the conditions associated with the action and returns
|
|
// the first error (if any).
|
|
func (c ActionConditions) Check(path string, setup Setup) error {
|
|
if len(c.Conditions) > 0 {
|
|
setup.Logf("checking action conditions")
|
|
}
|
|
for _, condition := range c.Conditions {
|
|
err := condition.Check(path, setup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OverwriteFileAction is used to overwrite a file.
|
|
type OverwriteFileAction struct {
|
|
ActionConditions
|
|
Params struct {
|
|
// Create determines whether the target directory
|
|
// will be created if it does not exist.
|
|
Create bool `json:"create"`
|
|
}
|
|
}
|
|
|
|
// Run implements plan.Action.Run.
|
|
func (a OverwriteFileAction) Run(path string, setup Setup) error {
|
|
setup.Logf("overwriting file %q", path)
|
|
src := setup.PathInRepo(SourceRepo, path)
|
|
dst := setup.PathInRepo(TargetRepo, path)
|
|
|
|
dstInfo, err := os.Stat(dst)
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
if !a.Params.Create {
|
|
return fmt.Errorf("path %q does not exist, not creating", dst)
|
|
}
|
|
case err != nil:
|
|
return fmt.Errorf("failed to check path %q: %v", dst, err)
|
|
case dstInfo.IsDir():
|
|
return fmt.Errorf("path %q is a directory", dst)
|
|
}
|
|
|
|
srcInfo, err := os.Stat(src)
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("file %q does not exist", src)
|
|
} else if err != nil {
|
|
return fmt.Errorf("failed to check path %q: %v", src, err)
|
|
}
|
|
if srcInfo.IsDir() {
|
|
return fmt.Errorf("path %q is a directory", src)
|
|
}
|
|
|
|
srcF, err := os.Open(src)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open %q: %v", src, err)
|
|
}
|
|
defer srcF.Close()
|
|
dstF, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, srcInfo.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open %q: %v", src, err)
|
|
}
|
|
defer dstF.Close()
|
|
_, err = io.Copy(dstF, srcF)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy file %q: %v", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OverwriteDirectoryAction is used to completely overwrite directories.
|
|
// If the target directory exists, it will be removed first.
|
|
type OverwriteDirectoryAction struct {
|
|
ActionConditions
|
|
Params struct {
|
|
// Create determines whether the target directory
|
|
// will be created if it does not exist.
|
|
Create bool `json:"create"`
|
|
}
|
|
}
|
|
|
|
// Run implements plan.Action.Run.
|
|
func (a OverwriteDirectoryAction) Run(path string, setup Setup) error {
|
|
setup.Logf("overwriting directory %q", path)
|
|
src := setup.PathInRepo(SourceRepo, path)
|
|
dst := setup.PathInRepo(TargetRepo, path)
|
|
|
|
dstInfo, err := os.Stat(dst)
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
if !a.Params.Create {
|
|
return fmt.Errorf("path %q does not exist, not creating", dst)
|
|
}
|
|
case err != nil:
|
|
return fmt.Errorf("failed to check path %q: %v", dst, err)
|
|
default:
|
|
if !dstInfo.IsDir() {
|
|
return fmt.Errorf("path %q is not a directory", dst)
|
|
}
|
|
err = os.RemoveAll(dst)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove directory %q: %v", dst, err)
|
|
}
|
|
}
|
|
|
|
srcInfo, err := os.Stat(src)
|
|
if os.IsNotExist(err) {
|
|
return fmt.Errorf("directory %q does not exist", src)
|
|
} else if err != nil {
|
|
return fmt.Errorf("failed to check path %q: %v", src, err)
|
|
}
|
|
if !srcInfo.IsDir() {
|
|
return fmt.Errorf("path %q is not a directory", src)
|
|
}
|
|
|
|
err = CopyDirectory(src, dst)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy path %q: %v", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CopyDirectory copies the directory src to dst so that after
|
|
// a successful operation the contents of src and dst are equal.
|
|
func CopyDirectory(src, dst string) error {
|
|
copier := dirCopier{dst: dst, src: src}
|
|
return filepath.Walk(src, copier.Copy)
|
|
}
|
|
|
|
type dirCopier struct {
|
|
dst string
|
|
src string
|
|
}
|
|
|
|
// Convert a path in the source directory to a path in the destination
|
|
// directory.
|
|
func (d dirCopier) srcToDst(path string) (string, error) {
|
|
suff := strings.TrimPrefix(path, d.src)
|
|
if suff == path {
|
|
return "", fmt.Errorf("path %q is not in %q", path, d.src)
|
|
}
|
|
return filepath.Join(d.dst, suff), nil
|
|
}
|
|
|
|
// Copy is an implementation of filepatch.WalkFunc that copies the
|
|
// source directory to target with all subdirectories.
|
|
func (d dirCopier) Copy(srcPath string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy directory: %v", err)
|
|
}
|
|
trgPath, err := d.srcToDst(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
err = os.MkdirAll(trgPath, info.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create directory %q: %v", trgPath, err)
|
|
}
|
|
err = os.Chtimes(trgPath, info.ModTime(), info.ModTime())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create directory %q: %v", trgPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
err = copyFile(srcPath, trgPath, info)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to copy file %q: %v", srcPath, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copyFile(src, dst string, info os.FileInfo) error {
|
|
srcF, err := os.Open(src)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open source file %q: %v", src, err)
|
|
}
|
|
defer srcF.Close()
|
|
dstF, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, info.Mode())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open destination file %q: %v", dst, err)
|
|
}
|
|
_, err = io.Copy(dstF, srcF)
|
|
if err != nil {
|
|
dstF.Close()
|
|
return fmt.Errorf("failed to copy file %q: %v", src, err)
|
|
}
|
|
if err = dstF.Close(); err != nil {
|
|
return fmt.Errorf("failed to close file %q: %v", dst, err)
|
|
}
|
|
err = os.Chtimes(dst, info.ModTime(), info.ModTime())
|
|
if err != nil {
|
|
return fmt.Errorf("failed to adjust file modification time for %q: %v", dst, err)
|
|
}
|
|
return nil
|
|
}
|