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

     1  package porter
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"sync"
     9  
    10  	"get.porter.sh/porter/pkg/build"
    11  	"get.porter.sh/porter/pkg/build/buildkit"
    12  	"get.porter.sh/porter/pkg/cache"
    13  	cnabtooci "get.porter.sh/porter/pkg/cnab/cnab-to-oci"
    14  	cnabprovider "get.porter.sh/porter/pkg/cnab/provider"
    15  	"get.porter.sh/porter/pkg/config"
    16  	"get.porter.sh/porter/pkg/mixin"
    17  	"get.porter.sh/porter/pkg/plugins"
    18  	"get.porter.sh/porter/pkg/secrets"
    19  	secretsplugin "get.porter.sh/porter/pkg/secrets/pluginstore"
    20  	"get.porter.sh/porter/pkg/signing"
    21  	signingplugin "get.porter.sh/porter/pkg/signing/pluginstore"
    22  	"get.porter.sh/porter/pkg/storage"
    23  	"get.porter.sh/porter/pkg/storage/migrations"
    24  	storageplugin "get.porter.sh/porter/pkg/storage/pluginstore"
    25  	"get.porter.sh/porter/pkg/templates"
    26  	"get.porter.sh/porter/pkg/tracing"
    27  	"github.com/hashicorp/go-multierror"
    28  )
    29  
    30  // Porter is the logic behind the porter client.
    31  type Porter struct {
    32  	*config.Config
    33  
    34  	// builder is loaded dynamically when unset, this allows us to
    35  	// use the configuration that is set after we create Porter,
    36  	// or to switch it out for tests.
    37  	builder build.Builder
    38  
    39  	Cache         cache.BundleCache
    40  	Credentials   storage.CredentialSetProvider
    41  	Parameters    storage.ParameterSetProvider
    42  	Sanitizer     *storage.Sanitizer
    43  	Installations storage.InstallationProvider
    44  	Registry      cnabtooci.RegistryProvider
    45  	Templates     *templates.Templates
    46  	Mixins        mixin.MixinProvider
    47  	Plugins       plugins.PluginProvider
    48  	CNAB          cnabprovider.CNABProvider
    49  	Secrets       secrets.Store
    50  	Storage       storage.Provider
    51  	Signer        signing.Signer
    52  }
    53  
    54  // New porter client, initialized with useful defaults.
    55  func New() *Porter {
    56  	c := config.New()
    57  	storage := storage.NewPluginAdapter(storageplugin.NewStore(c))
    58  	secretStorage := secrets.NewPluginAdapter(secretsplugin.NewStore(c))
    59  	signer := signing.NewPluginAdapter(signingplugin.NewSigner(c))
    60  	return NewFor(c, storage, secretStorage, signer)
    61  }
    62  
    63  func NewFor(c *config.Config, store storage.Store, secretStorage secrets.Store, signer signing.Signer) *Porter {
    64  	cache := cache.New(c)
    65  
    66  	storageManager := migrations.NewManager(c, store)
    67  	installationStorage := storage.NewInstallationStore(storageManager)
    68  	credStorage := storage.NewCredentialStore(storageManager, secretStorage)
    69  	paramStorage := storage.NewParameterStore(storageManager, secretStorage)
    70  	sanitizerService := storage.NewSanitizer(paramStorage, secretStorage)
    71  
    72  	storageManager.Initialize(sanitizerService) // we have a bit of a dependency problem here that it would be great to figure out eventually
    73  
    74  	return &Porter{
    75  		Config:        c,
    76  		Cache:         cache,
    77  		Storage:       storageManager,
    78  		Installations: installationStorage,
    79  		Credentials:   credStorage,
    80  		Parameters:    paramStorage,
    81  		Secrets:       secretStorage,
    82  		Registry:      cnabtooci.NewRegistry(c.Context),
    83  		Templates:     templates.NewTemplates(c),
    84  		Mixins:        mixin.NewPackageManager(c),
    85  		Plugins:       plugins.NewPackageManager(c),
    86  		CNAB:          cnabprovider.NewRuntime(c, installationStorage, credStorage, paramStorage, secretStorage, sanitizerService),
    87  		Sanitizer:     sanitizerService,
    88  		Signer:        signer,
    89  	}
    90  }
    91  
    92  // Used to warn just a single time when Porter starts up.
    93  // Connect is called more than once, and this helps us validate certain things, like build flags, a single time only.
    94  var initWarnings sync.Once
    95  
    96  // Connect initializes Porter for use and must be called before other Porter methods.
    97  // It is the responsibility of the caller to also call Close when done with Porter.
    98  func (p *Porter) Connect(ctx context.Context) (context.Context, error) {
    99  	initWarnings.Do(func() {
   100  		// Check if this is a special dev build that will trace sensitive data and strongly warn people
   101  		if tracing.IsTraceSensitiveAttributesEnabled() {
   102  			fmt.Fprintln(p.Err, "🚨 WARNING! This is a custom developer build of Porter with the traceSensitiveAttributes build flag set. "+
   103  				"Porter will include sensitive data, such as parameters and credentials, in the telemetry trace data. "+
   104  				"This build flag should only be used for local development only. "+
   105  				"If you didn't intend to use a custom build of Porter with this flag enabled, reinstall Porter using the official builds from https://porter.sh/install.")
   106  		}
   107  	})
   108  
   109  	// Load the config file and replace any referenced secrets
   110  	return p.Config.Load(ctx, func(innerCtx context.Context, secret string) (string, error) {
   111  		value, err := p.Secrets.Resolve(innerCtx, "secret", secret)
   112  		if err != nil {
   113  			if strings.Contains(err.Error(), "invalid value source: secret") {
   114  				return "", errors.New("No secret store account is configured")
   115  			}
   116  			return "", err
   117  		}
   118  		return value, nil
   119  	})
   120  }
   121  
   122  // Close releases resources used by Porter before terminating the application.
   123  func (p *Porter) Close() error {
   124  	// Shutdown our plugins
   125  	var bigErr *multierror.Error
   126  
   127  	err := p.Secrets.Close()
   128  	if err != nil {
   129  		bigErr = multierror.Append(bigErr, err)
   130  	}
   131  
   132  	err = p.Storage.Close()
   133  	if err != nil {
   134  		bigErr = multierror.Append(bigErr, err)
   135  	}
   136  
   137  	err = p.Config.Close()
   138  	if err != nil {
   139  		bigErr = multierror.Append(bigErr, err)
   140  	}
   141  
   142  	err = p.Signer.Close()
   143  	if err != nil {
   144  		bigErr = multierror.Append(bigErr, err)
   145  	}
   146  
   147  	return bigErr.ErrorOrNil()
   148  }
   149  
   150  // GetBuilder creates a Builder based on the current configuration.
   151  func (p *Porter) GetBuilder(ctx context.Context) build.Builder {
   152  	log := tracing.LoggerFromContext(ctx)
   153  
   154  	if p.builder == nil {
   155  		driver := p.GetBuildDriver()
   156  		switch driver {
   157  		case config.BuildDriverBuildkit:
   158  			// supported, yay!
   159  		case config.BuildDriverDocker:
   160  			log.Warn("The docker build driver is no longer supported. Using buildkit instead.")
   161  		default:
   162  			log.Warnf("Unsupported build driver: %s. Using buildkit instead.", driver)
   163  		}
   164  		p.builder = buildkit.NewBuilder(p.Config)
   165  	}
   166  	return p.builder
   167  }