...

Source file src/edge-infra.dev/pkg/f8n/warehouse/lift/pack/context.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/lift/pack

     1  package pack
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"go.uber.org/multierr"
     9  	"gopkg.in/yaml.v2"
    10  	"sigs.k8s.io/kustomize/api/krusty"
    11  	ktypes "sigs.k8s.io/kustomize/api/types"
    12  	"sigs.k8s.io/kustomize/kyaml/filesys"
    13  
    14  	"edge-infra.dev/pkg/f8n/warehouse/cluster"
    15  	"edge-infra.dev/pkg/f8n/warehouse/lift"
    16  	"edge-infra.dev/pkg/f8n/warehouse/lift/pack/filters"
    17  	"edge-infra.dev/pkg/f8n/warehouse/lift/pack/types"
    18  	"edge-infra.dev/pkg/k8s/kustomize"
    19  )
    20  
    21  // Context specifies the supporting context for packing Pallets.
    22  type Context struct {
    23  	*lift.Config // Warehouse environment configuration.
    24  
    25  	// FS is the FileSystem that Kustomizer will be executed against. Defaults to
    26  	// on-disk FileSystem if not provided.
    27  	FS filesys.FileSystem
    28  
    29  	// Kustomizer is the kustomize engine to use when building manifests.
    30  	// A default Kustomizer is instantiated if not provided.
    31  	Kustomizer *krusty.Kustomizer
    32  
    33  	// Filters are YAML filters that are applied to the manifests during packing.
    34  	// The default built-in filters will be used if not provided.
    35  	Filters []filters.Filter
    36  }
    37  
    38  // Default fills in default values for unset fields and returns the updated
    39  // Options object.
    40  func (c Context) Default() (Context, error) {
    41  	if c.Config == nil {
    42  		cfg, err := lift.NewConfig()
    43  		if err != nil {
    44  			return Context{}, err
    45  		}
    46  		c.Config = &cfg
    47  	}
    48  
    49  	if c.FS == nil {
    50  		c.FS = &kustomize.FS{FS: os.DirFS(c.WAREHOUSEPATH)}
    51  	}
    52  
    53  	if c.Kustomizer == nil {
    54  		c.Kustomizer = krusty.MakeKustomizer(
    55  			&krusty.Options{
    56  				LoadRestrictions: ktypes.LoadRestrictionsNone,
    57  				PluginConfig:     ktypes.DisabledPluginConfig(),
    58  				Reorder:          krusty.ReorderOptionLegacy,
    59  			},
    60  		)
    61  	}
    62  
    63  	return c, nil
    64  }
    65  
    66  // ReadPkgFile reads the package definition at the given path.
    67  func (c Context) ReadPkgFile(path string) ([]byte, error) {
    68  	if c.FS.IsDir(path) && c.FS.Exists(path) {
    69  		for _, file := range types.Files {
    70  			p := filepath.Join(path, file)
    71  			if c.FS.Exists(p) {
    72  				return c.FS.ReadFile(p)
    73  			}
    74  		}
    75  	}
    76  
    77  	return nil, fmt.Errorf(
    78  		"invalid build path: %s does not contain expected files: %s",
    79  		path, types.Files,
    80  	)
    81  }
    82  
    83  // LoadPkg attempts to load a package definition for a Pallet package at the
    84  // input path.
    85  func (c Context) LoadPkg(path string) (types.Pallet, error) {
    86  	cfg := &types.Pallet{}
    87  	data, err := c.ReadPkgFile(path)
    88  	if err != nil {
    89  		return types.Pallet{}, fmt.Errorf(
    90  			"failed to read package file %s: %w",
    91  			path,
    92  			err,
    93  		)
    94  	}
    95  
    96  	if err := cfg.Unmarshal(data); err != nil {
    97  		return types.Pallet{}, fmt.Errorf(
    98  			"failed to unmarshal data from %s: %w",
    99  			path,
   100  			err,
   101  		)
   102  	}
   103  	if err := cfg.IsValid(); err != nil {
   104  		return types.Pallet{}, fmt.Errorf(
   105  			"invalid config at %s: %w",
   106  			path,
   107  			err,
   108  		)
   109  	}
   110  	if err := validateClusterProviders(*cfg, c.ClusterProviders); err != nil {
   111  		return types.Pallet{}, fmt.Errorf(
   112  			"invalid cluster providers at %s: %w",
   113  			path,
   114  			err,
   115  		)
   116  	}
   117  
   118  	return *cfg, nil
   119  }
   120  
   121  // LoadConfig loads a Warehouse configuration file at the input path from the
   122  // context's filesystem.
   123  func (c Context) LoadConfig(path string) (lift.Config, error) {
   124  	cfg := lift.Config{}
   125  	data, err := c.FS.ReadFile(path)
   126  	if err != nil {
   127  		return cfg, err
   128  	}
   129  	if err := yaml.Unmarshal(data, &cfg); err != nil {
   130  		return cfg, err
   131  	}
   132  	return cfg, nil
   133  }
   134  
   135  // ResolveConfigPath returns the default configuration file path if the input
   136  // is a directory. Otherwise it returns the input path. It does not validate
   137  // that the file exists.
   138  func (c Context) ResolveConfigPath(path string) string {
   139  	if c.FS.IsDir(path) {
   140  		return filepath.Join(path, lift.ConfigFile)
   141  	}
   142  	return path
   143  }
   144  
   145  func (c Context) Capabilities() []lift.CapabilityConfig {
   146  	return append(c.Config.Runtime.Capabilities, c.Infrastructure)
   147  }
   148  
   149  // validateClusterProviders checks the package definition against the ClusterProviders
   150  // configured for this execution context.
   151  func validateClusterProviders(pkg types.Pallet, providers cluster.Providers) error {
   152  	var errs []error
   153  
   154  	// check for valid cluster providers
   155  	var pkgProviders cluster.Providers
   156  	for _, t := range pkg.Kustomize {
   157  		for _, p := range t.Providers {
   158  			pkgProviders = append(pkgProviders, p)
   159  			if err := providers.IsValid(p); err != nil {
   160  				errs = append(errs, fmt.Errorf(
   161  					"spec.kustomize: %w", err,
   162  				))
   163  			}
   164  		}
   165  	}
   166  	if len(pkgProviders) > len(providers) {
   167  		errs = append(errs, fmt.Errorf(
   168  			"too many providers across all spec.kustomize entries: [%s]. "+
   169  				"a provider can only be used once", pkgProviders,
   170  		))
   171  	}
   172  
   173  	for _, p := range pkg.Providers {
   174  		if err := providers.IsValid(p); err != nil {
   175  			errs = append(errs, fmt.Errorf(
   176  				"spec.providers contains invalid value: %w", err,
   177  			))
   178  		}
   179  	}
   180  
   181  	return multierr.Combine(errs...)
   182  }
   183  

View as plain text