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 }