Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storage): retain file permission and owner when adding via confi… #589

Merged
merged 10 commits into from
Dec 6, 2024
25 changes: 19 additions & 6 deletions pkg/instance/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package instance
import (
"context"
"fmt"
"os"
"path/filepath"
"sync"

Expand All @@ -14,8 +15,6 @@ import (
"github.com/celestiaorg/knuu/pkg/container"
)

const buildDirBase = "/tmp/knuu"

type build struct {
instance *Instance
imageName string
Expand Down Expand Up @@ -53,10 +52,15 @@ func (b *build) SetImage(ctx context.Context, image string, args ...builder.ArgI
return ErrSettingImageNotAllowed.WithParams(b.instance.state.String())
}

buildDir, err := b.getBuildDir()
if err != nil {
return ErrGettingBuildDir.Wrap(err)
}

// Use the builder to build a new image
factory, err := container.NewBuilderFactory(container.BuilderFactoryOptions{
ImageName: image,
BuildContext: b.getBuildDir(),
BuildContext: buildDir,
ImageBuilder: b.instance.ImageBuilder,
Args: args,
Logger: b.instance.Logger,
Expand Down Expand Up @@ -86,9 +90,14 @@ func (b *build) SetGitRepo(ctx context.Context, gitContext builder.GitContext, a
return ErrGettingImageName.Wrap(err)
}

buildDir, err := b.getBuildDir()
if err != nil {
return ErrGettingBuildDir.Wrap(err)
}

factory, err := container.NewBuilderFactory(container.BuilderFactoryOptions{
ImageName: imageName,
BuildContext: b.getBuildDir(),
BuildContext: buildDir,
ImageBuilder: b.instance.ImageBuilder,
Args: args,
Logger: b.instance.Logger,
Expand Down Expand Up @@ -225,8 +234,12 @@ func getImageRegistry(imageName string) (string, error) {
}

// getBuildDir returns the build directory for the instance
func (b *build) getBuildDir() string {
return filepath.Join(buildDirBase, b.instance.name)
func (b *build) getBuildDir() (string, error) {
tmpDir, err := os.MkdirTemp("", "knuu-build-*")
smuu marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return "", err
}
return filepath.Join(tmpDir, b.instance.name), nil
}

// addFileToBuilder adds a file to the builder
Expand Down
3 changes: 3 additions & 0 deletions pkg/instance/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var (
ErrSettingGitRepo = errors.New("SettingGitRepo", "setting git repo is only allowed in state 'None'. Current state is '%s'")
ErrGettingBuildContext = errors.New("GettingBuildContext", "error getting build context")
ErrGettingImageName = errors.New("GettingImageName", "error getting image name")
ErrGettingBuildDir = errors.New("GettingBuildDir", "error getting build directory")
ErrSettingImageNotAllowedForSidecars = errors.New("SettingImageNotAllowedForSidecars", "setting image is not allowed for sidecars")
ErrSettingCommand = errors.New("SettingCommand", "setting command is only allowed in state 'Preparing' or 'Committed'. Current state is '%s")
ErrSettingArgsNotAllowed = errors.New("SettingArgsNotAllowed", "setting args is only allowed in state 'Preparing' or 'Committed'. Current state is '%s")
Expand All @@ -102,7 +103,9 @@ var (
ErrCreatingDirectory = errors.New("CreatingDirectory", "error creating directory")
ErrFailedToCreateDestFile = errors.New("FailedToCreateDestFile", "failed to create destination file '%s'")
ErrFailedToOpenSrcFile = errors.New("FailedToOpenSrcFile", "failed to open source file '%s'")
ErrFailedToGetSrcFileInfo = errors.New("FailedToGetSrcFileInfo", "failed to get source file info for")
mojtaba-esk marked this conversation as resolved.
Show resolved Hide resolved
ErrFailedToCopyFile = errors.New("FailedToCopyFile", "failed to copy from source '%s' to destination '%s'")
ErrFailedToSetPermissions = errors.New("FailedToSetPermissions", "failed to set permissions for destination file")
ErrSrcDoesNotExistOrIsDirectory = errors.New("SrcDoesNotExistOrIsDirectory", "src '%s' does not exist or is a directory")
ErrInvalidFormat = errors.New("InvalidFormat", "invalid format")
ErrFailedToConvertToInt64 = errors.New("FailedToConvertToInt64", "failed to convert to int64")
Expand Down
1 change: 0 additions & 1 deletion pkg/instance/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ func (e *execution) prepareReplicaSetConfig() k8s.ReplicaSetConfig {
Name: e.instance.name,
Labels: e.Labels(),
ServiceAccountName: e.instance.name,
FsGroup: e.instance.storage.fsGroup,
ContainerConfig: containerConfig,
SidecarConfigs: sidecarConfigs,
}
Expand Down
12 changes: 5 additions & 7 deletions pkg/instance/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,12 @@ func (r *resources) deployStorage(ctx context.Context) error {
return ErrDeployingVolumeForInstance.WithParams(r.instance.name).Wrap(err)
}
}
if len(r.instance.storage.files) == 0 {
return nil
if len(r.instance.storage.files) != 0 {
if err := r.instance.storage.deployFiles(ctx); err != nil {
return ErrDeployingFilesForInstance.WithParams(r.instance.name).Wrap(err)
}
}

if err := r.instance.storage.deployFiles(ctx); err != nil {
return ErrDeployingFilesForInstance.WithParams(r.instance.name).Wrap(err)
}
return nil
}

Expand Down Expand Up @@ -123,8 +122,7 @@ func (r *resources) destroyResources(ctx context.Context) error {
}

if len(r.instance.storage.files) != 0 {
err := r.instance.storage.destroyFiles(ctx)
if err != nil {
if err := r.instance.storage.destroyFiles(ctx); err != nil {
return ErrDestroyingFilesForInstance.WithParams(r.instance.name).Wrap(err)
}
}
Expand Down
80 changes: 53 additions & 27 deletions pkg/instance/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ type storage struct {
instance *Instance
volumes []*k8s.Volume
files []*k8s.File
fsGroup int64
}

func (i *Instance) Storage() *storage {
Expand All @@ -41,7 +40,7 @@ func (s *storage) AddFile(src string, dest string, chown string) error {
return err
}

dstPath, err := s.copyFileToBuildDir(src, dest)
buildDirPath, err := s.copyFileToBuildDir(src, dest)
if err != nil {
return err
}
Expand All @@ -51,14 +50,18 @@ func (s *storage) AddFile(src string, dest string, chown string) error {
s.instance.build.addFileToBuilder(src, dest, chown)
return nil
case StateCommitted, StateStopped:
return s.addFileToInstance(dstPath, dest, chown)
return s.addFileToInstance(buildDirPath, dest, chown)
}

buildDir, err := s.instance.build.getBuildDir()
if err != nil {
return ErrGettingBuildDir.Wrap(err)
}
s.instance.Logger.WithFields(logrus.Fields{
"file": dest,
"instance": s.instance.name,
"state": s.instance.state,
"build_dir": s.instance.build.getBuildDir(),
"build_dir": buildDir,
}).Debug("added file")
return nil
}
Expand Down Expand Up @@ -92,7 +95,11 @@ func (s *storage) AddFolder(src string, dest string, chown string) error {
if err != nil {
return err
}
dstPath := filepath.Join(s.instance.build.getBuildDir(), dest, relPath)
buildDir, err := s.instance.build.getBuildDir()
if err != nil {
return ErrGettingBuildDir.Wrap(err)
}
dstPath := filepath.Join(buildDir, dest, relPath)

if info.IsDir() {
// create directory at destination path
Expand All @@ -106,11 +113,15 @@ func (s *storage) AddFolder(src string, dest string, chown string) error {
return ErrCopyingFolderToInstance.WithParams(src, s.instance.name).Wrap(err)
}

buildDir, err := s.instance.build.getBuildDir()
if err != nil {
return ErrGettingBuildDir.Wrap(err)
}
s.instance.Logger.WithFields(logrus.Fields{
"folder": dest,
"instance": s.instance.name,
"state": s.instance.state,
"build_dir": s.instance.build.getBuildDir(),
"build_dir": buildDir,
}).Debug("added folder")
return nil
}
Expand All @@ -131,6 +142,9 @@ func (s *storage) AddFileBytes(bytes []byte, dest string, chown string) error {
if _, err := tmpfile.Write(bytes); err != nil {
return err
}
if err := tmpfile.Chmod(0644); err != nil {
smuu marked this conversation as resolved.
Show resolved Hide resolved
return err
}
if err := tmpfile.Close(); err != nil {
return err
}
Expand Down Expand Up @@ -244,51 +258,64 @@ func (s *storage) validateFileArgs(src, dest, chown string) error {
}

func (s *storage) copyFileToBuildDir(src, dest string) (string, error) {
dstPath := filepath.Join(s.instance.build.getBuildDir(), dest)
buildDir, err := s.instance.build.getBuildDir()
if err != nil {
return "", ErrGettingBuildDir.Wrap(err)
}
dstPath := filepath.Join(buildDir, dest)
if err := os.MkdirAll(filepath.Dir(dstPath), os.ModePerm); err != nil {
return "", ErrCreatingDirectory.Wrap(err)
}

dst, err := os.Create(dstPath)
if err != nil {
return "", ErrFailedToCreateDestFile.WithParams(dstPath).Wrap(err)
}
defer dst.Close()

srcFile, err := os.Open(src)
if err != nil {
return "", ErrFailedToOpenSrcFile.WithParams(src).Wrap(err)
}
defer srcFile.Close()

srcInfo, err := srcFile.Stat()
if err != nil {
return "", ErrFailedToGetSrcFileInfo.WithParams(src).Wrap(err)
}

dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY, srcInfo.Mode().Perm())
if err != nil {
return "", ErrFailedToCreateDestFile.WithParams(dstPath).Wrap(err)
}
defer dst.Close()

if _, err := io.Copy(dst, srcFile); err != nil {
return "", ErrFailedToCopyFile.WithParams(src, dstPath).Wrap(err)
}

// Ensure the destination file has the same permissions as the source file
if err := os.Chmod(dstPath, srcInfo.Mode().Perm()); err != nil {
mojtaba-esk marked this conversation as resolved.
Show resolved Hide resolved
return "", ErrFailedToSetPermissions.WithParams(dstPath).Wrap(err)
}

return dstPath, nil
}

func (s *storage) addFileToInstance(dstPath, dest, chown string) error {
srcInfo, err := os.Stat(dstPath)
func (s *storage) addFileToInstance(srcPath, dest, chown string) error {
srcInfo, err := os.Stat(srcPath)
if os.IsNotExist(err) || srcInfo.IsDir() {
return ErrSrcDoesNotExistOrIsDirectory.WithParams(dstPath).Wrap(err)
return ErrSrcDoesNotExistOrIsDirectory.WithParams(srcPath).Wrap(err)
}

file := s.instance.K8sClient.NewFile(dstPath, dest)
// get the permission of the src file
permission := fmt.Sprintf("%o", srcInfo.Mode().Perm())

parts := strings.Split(chown, ":")
if len(parts) != 2 {
return ErrInvalidFormat
return ErrInvalidFormat.WithParams(chown)
}

group, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
return ErrFailedToConvertToInt64.Wrap(err)
for _, part := range parts {
smuu marked this conversation as resolved.
Show resolved Hide resolved
if _, err := strconv.ParseInt(part, 10, 64); err != nil {
return ErrFailedToConvertToInt64.WithParams(part).Wrap(err)
}
}
file := s.instance.K8sClient.NewFile(srcPath, dest, chown, permission)
smuu marked this conversation as resolved.
Show resolved Hide resolved
smuu marked this conversation as resolved.
Show resolved Hide resolved

if s.fsGroup != 0 && s.fsGroup != group {
return ErrAllFilesMustHaveSameGroup
}
s.fsGroup = group
s.files = append(s.files, file)
return nil
}
Expand Down Expand Up @@ -439,6 +466,5 @@ func (s *storage) clone() *storage {
instance: nil,
volumes: volumesCopy,
files: filesCopy,
fsGroup: s.fsGroup,
}
}
Loading
Loading