get.porter.sh/porter@v1.3.0/pkg/pkgmgmt/client/filesystem.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"path/filepath"
    10  
    11  	"get.porter.sh/porter/pkg/config"
    12  	"get.porter.sh/porter/pkg/pkgmgmt"
    13  	"get.porter.sh/porter/pkg/portercontext"
    14  	"get.porter.sh/porter/pkg/tracing"
    15  	"go.opentelemetry.io/otel/attribute"
    16  	"go.uber.org/zap/zapcore"
    17  )
    18  
    19  var _ pkgmgmt.PackageManager = &FileSystem{}
    20  
    21  type PackageMetadataBuilder func() pkgmgmt.PackageMetadata
    22  
    23  func NewFileSystem(config *config.Config, pkgType string) *FileSystem {
    24  	return &FileSystem{
    25  		Config:        config,
    26  		PackageType:   pkgType,
    27  		BuildMetadata: func() pkgmgmt.PackageMetadata { return pkgmgmt.Metadata{} },
    28  	}
    29  }
    30  
    31  type FileSystem struct {
    32  	*config.Config
    33  
    34  	// PackageType is the type of package managed by this instance of the
    35  	// package manager. It must also correspond to the directory name in
    36  	// PORTER_HOME.
    37  	PackageType string
    38  
    39  	// PreRun is executed before commands are run against a package, giving
    40  	// consumers a chance to tweak the command first.
    41  	PreRun pkgmgmt.PreRunHandler
    42  
    43  	// BuildMetadata allows mixins/plugins to supply the proper struct that
    44  	// represents its package metadata.
    45  	BuildMetadata PackageMetadataBuilder
    46  }
    47  
    48  func (fs *FileSystem) List() ([]string, error) {
    49  	parentDir, err := fs.GetPackagesDir()
    50  	if err != nil {
    51  		return nil, fmt.Errorf("could not get package directory:%w", err)
    52  	}
    53  
    54  	files, err := fs.FileSystem.ReadDir(parentDir)
    55  	if err != nil {
    56  		return nil, fmt.Errorf("could not list the contents of the %s directory %q: %w", fs.PackageType, parentDir, err)
    57  	}
    58  
    59  	names := make([]string, 0, len(files))
    60  	for _, file := range files {
    61  		if !file.IsDir() {
    62  			continue
    63  		}
    64  
    65  		names = append(names, file.Name())
    66  	}
    67  
    68  	return names, nil
    69  }
    70  
    71  func (fs *FileSystem) GetMetadata(ctx context.Context, name string) (pkgmgmt.PackageMetadata, error) {
    72  	ctx, span := tracing.StartSpan(ctx, attribute.String("package.type", fs.PackageType), attribute.String("package.name", name))
    73  	defer span.EndSpan()
    74  
    75  	pkgDir, err := fs.GetPackageDir(name)
    76  	if err != nil {
    77  		return nil, span.Error(err)
    78  	}
    79  	r := NewRunner(name, pkgDir, false)
    80  
    81  	// Copy the existing context and tweak to pipe the output differently
    82  	jsonB := &bytes.Buffer{}
    83  	pkgContext := *fs.Context
    84  	pkgContext.Out = jsonB
    85  	if span.ShouldLog(zapcore.DebugLevel) {
    86  		pkgContext.Err = io.Discard
    87  	}
    88  	r.Context = &pkgContext
    89  
    90  	cmd := pkgmgmt.CommandOptions{Command: "version --output json", PreRun: fs.PreRun}
    91  	err = r.Run(ctx, cmd)
    92  	if err != nil {
    93  		return nil, span.Error(err)
    94  	}
    95  
    96  	result := fs.BuildMetadata()
    97  	err = json.Unmarshal(jsonB.Bytes(), &result)
    98  	if err != nil {
    99  		return nil, span.Error(err)
   100  	}
   101  
   102  	return result, nil
   103  }
   104  
   105  func (fs *FileSystem) Run(ctx context.Context, pkgContext *portercontext.Context, name string, commandOpts pkgmgmt.CommandOptions) error {
   106  	pkgDir, err := fs.GetPackageDir(name)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	r := NewRunner(name, pkgDir, commandOpts.Runtime)
   112  	r.Context = pkgContext
   113  
   114  	err = r.Validate()
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	commandOpts.PreRun = fs.PreRun
   120  	return r.Run(ctx, commandOpts)
   121  }
   122  
   123  func (fs *FileSystem) GetPackagesDir() (string, error) {
   124  	home, err := fs.GetHomeDir()
   125  	if err != nil {
   126  		return "", err
   127  	}
   128  
   129  	return filepath.Join(home, fs.PackageType), nil
   130  }
   131  
   132  func (fs *FileSystem) GetPackageDir(name string) (string, error) {
   133  	parentDir, err := fs.GetPackagesDir()
   134  	if err != nil {
   135  		return "", err
   136  	}
   137  
   138  	pkgDir := filepath.Join(parentDir, name)
   139  	dirExists, err := fs.FileSystem.DirExists(pkgDir)
   140  	if err != nil {
   141  		return "", fmt.Errorf("%s %s not accessible at %s: %w", fs.PackageType, name, pkgDir, err)
   142  	}
   143  	if !dirExists {
   144  		return "", fmt.Errorf("%s %s not installed in %s", fs.PackageType, name, pkgDir)
   145  	}
   146  
   147  	return pkgDir, nil
   148  }
   149  
   150  func (fs *FileSystem) BuildClientPath(pkgDir string, name string) string {
   151  	return filepath.Join(pkgDir, name) + pkgmgmt.FileExt
   152  }