get.porter.sh/porter@v1.3.0/pkg/porter/cnab.go (about)

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"path/filepath"
     8  
     9  	"get.porter.sh/porter/pkg/build"
    10  	cnabprovider "get.porter.sh/porter/pkg/cnab/provider"
    11  	"get.porter.sh/porter/pkg/config"
    12  	"get.porter.sh/porter/pkg/portercontext"
    13  )
    14  
    15  const (
    16  	// DockerDriver is the name of the Docker driver.
    17  	DockerDriver = cnabprovider.DriverNameDocker
    18  
    19  	// DebugDriver is the name of the Debug driver.
    20  	DebugDriver = cnabprovider.DriverNameDebug
    21  
    22  	// DefaultDriver is the name of the default driver (Docker).
    23  	DefaultDriver = DockerDriver
    24  )
    25  
    26  type BundleDefinitionOptions struct {
    27  	// File path to the porter manifest. Defaults to the bundle in the current directory.
    28  	File string
    29  
    30  	// CNABFile is the path to the bundle.json file. Cannot be specified at the same time as the porter manifest or a tag.
    31  	CNABFile string
    32  
    33  	// RelocationMapping is the path to the relocation-mapping.json file, if one exists. Populated only for published bundles
    34  	RelocationMapping string
    35  
    36  	// ReferenceSet indicates whether a bundle reference is present, to determine whether or not to default bundle files
    37  	ReferenceSet bool
    38  
    39  	// Dir represents the build context directory containing bundle assets
    40  	Dir string
    41  
    42  	// AutoBuildDisabled indicates that Porter should not check if the bundle
    43  	// definition has changed since it was last built and automatically build before
    44  	// executing the requested command.
    45  	AutoBuildDisabled bool
    46  
    47  	// PreserveTags keep the original tag name on referenced images.
    48  	PreserveTags bool
    49  }
    50  
    51  func (o *BundleDefinitionOptions) Validate(cxt *portercontext.Context) error {
    52  	var err error
    53  
    54  	if o.ReferenceSet {
    55  		return nil
    56  	}
    57  
    58  	// Resolve the proper build context directory
    59  	if o.Dir != "" {
    60  		_, err = cxt.FileSystem.IsDir(o.Dir)
    61  		if err != nil {
    62  			return fmt.Errorf("%q is not a valid directory: %w", o.Dir, err)
    63  		}
    64  		o.Dir = cxt.FileSystem.Abs(o.Dir)
    65  	} else {
    66  		// default to current working directory
    67  		o.Dir = cxt.Getwd()
    68  	}
    69  
    70  	if o.File != "" {
    71  		if !filepath.IsAbs(o.File) {
    72  			o.File = cxt.FileSystem.Abs(filepath.Join(o.Dir, o.File))
    73  		} else {
    74  			o.File = cxt.FileSystem.Abs(o.File)
    75  		}
    76  	}
    77  
    78  	err = o.validateBundleFiles(cxt)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	err = o.defaultBundleFiles(cxt)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	// Enter the resolved build context directory after all defaults
    89  	// have been populated
    90  	cxt.Chdir(o.Dir)
    91  	return nil
    92  }
    93  
    94  // installationOptions are common options that apply to commands that use an installation
    95  type installationOptions struct {
    96  	BundleDefinitionOptions
    97  
    98  	// Namespace of the installation.
    99  	Namespace string
   100  
   101  	// Name of the installation. Defaults to the name of the bundle.
   102  	Name string
   103  }
   104  
   105  // Validate prepares for an action and validates the options.
   106  // For example, relative paths are converted to full paths and then checked that
   107  // they exist and are accessible.
   108  func (o *installationOptions) Validate(ctx context.Context, args []string, p *Porter) error {
   109  	err := o.validateInstallationName(args)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	err = o.BundleDefinitionOptions.Validate(p.Context)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	err = p.applyDefaultOptions(ctx, o)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  // validateInstallationName grabs the installation name from the first positional argument.
   128  func (o *installationOptions) validateInstallationName(args []string) error {
   129  	if len(args) == 1 {
   130  		o.Name = args[0]
   131  	} else if len(args) > 1 {
   132  		return fmt.Errorf("only one positional argument may be specified, the installation name, but multiple were received: %s", args)
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // defaultBundleFiles defaults the porter manifest and the bundle.json files.
   139  func (o *BundleDefinitionOptions) defaultBundleFiles(cxt *portercontext.Context) error {
   140  	if o.File != "" { // --file
   141  		o.defaultCNABFile()
   142  	} else if o.CNABFile != "" { // --cnab-file
   143  		// Nothing to default
   144  	} else {
   145  		defaultPath := filepath.Join(o.Dir, config.Name)
   146  		manifestExists, err := cxt.FileSystem.Exists(defaultPath)
   147  		if err != nil {
   148  			return fmt.Errorf("could not find a porter manifest at %s: %w", defaultPath, err)
   149  		} else if !manifestExists {
   150  			return nil
   151  		}
   152  
   153  		o.File = defaultPath
   154  		o.defaultCNABFile()
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func (o *BundleDefinitionOptions) defaultCNABFile() {
   161  	o.CNABFile = filepath.Join(o.Dir, build.LOCAL_BUNDLE)
   162  }
   163  
   164  func (o *BundleDefinitionOptions) validateBundleFiles(cxt *portercontext.Context) error {
   165  	if o.File != "" && o.CNABFile != "" {
   166  		return errors.New("cannot specify both --file and --cnab-file")
   167  	}
   168  
   169  	err := o.validateFile(cxt)
   170  	if err != nil {
   171  		return err
   172  	}
   173  
   174  	err = o.validateCNABFile(cxt)
   175  	if err != nil {
   176  		return err
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  func (o *BundleDefinitionOptions) validateFile(cxt *portercontext.Context) error {
   183  	if o.File == "" {
   184  		return nil
   185  	}
   186  
   187  	// Verify the file can be accessed
   188  	if _, err := cxt.FileSystem.Stat(o.File); err != nil {
   189  		return fmt.Errorf("unable to access --file %s: %w", o.File, err)
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  // validateCNABFile converts the bundle file path to an absolute filepath and verifies that it exists.
   196  func (o *BundleDefinitionOptions) validateCNABFile(cxt *portercontext.Context) error {
   197  	if o.CNABFile == "" {
   198  		return nil
   199  	}
   200  
   201  	originalPath := o.CNABFile
   202  	if !filepath.IsAbs(o.CNABFile) {
   203  		// Convert to an absolute filepath because runtime needs it that way
   204  		o.CNABFile = filepath.Join(cxt.Getwd(), o.CNABFile)
   205  	}
   206  
   207  	// Verify the file can be accessed
   208  	if _, err := cxt.FileSystem.Stat(o.CNABFile); err != nil {
   209  		// warn about the original relative path
   210  		return fmt.Errorf("unable to access --cnab-file %s: %w", originalPath, err)
   211  	}
   212  
   213  	return nil
   214  }