mirror of https://github.com/knative/func.git
refactor: config path accessors with instantiation cleanup (#686)
* feat: config and repository path creation Removes need to use a client to trigger creation of paths Adds back static path accessors Enables creation of paths when configured repos is outside config Cleans up instantiation logic, including removal of some setters * fix spelling mistakes per review
This commit is contained in:
parent
2f241824ff
commit
92ac14a6f7
124
client.go
124
client.go
|
@ -8,6 +8,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
|
@ -26,12 +27,17 @@ const (
|
|||
// DefaultVersion is the initial value for string members whose implicit type
|
||||
// is a semver.
|
||||
DefaultVersion = "0.0.0"
|
||||
|
||||
// DefaultConfigPath is used in the unlikely event that
|
||||
// the user has no home directory (no ~), there is no
|
||||
// XDG_CONFIG_HOME set, and no WithConfigPath was used.
|
||||
DefaultConfigPath = ".config/func"
|
||||
)
|
||||
|
||||
// Client for managing Function instances.
|
||||
type Client struct {
|
||||
repositories *Repositories // Repositories management
|
||||
templates *Templates // Templates management
|
||||
repositoriesPath string // path to repositories
|
||||
repositoriesURI string // repo URI (overrides repositories path)
|
||||
verbose bool // print verbose logs
|
||||
builder Builder // Builds a runnable image source
|
||||
pusher Pusher // Pushes Funcation image to a remote
|
||||
|
@ -44,6 +50,8 @@ type Client struct {
|
|||
registry string // default registry for OCI image tags
|
||||
progressListener ProgressListener // progress listener
|
||||
emitter Emitter // Emits CloudEvents to functions
|
||||
repositories *Repositories // Repositories management
|
||||
templates *Templates // Templates management
|
||||
}
|
||||
|
||||
// ErrNotBuilt indicates the Function has not yet been built.
|
||||
|
@ -161,14 +169,8 @@ type Emitter interface {
|
|||
|
||||
// New client for Function management.
|
||||
func New(options ...Option) *Client {
|
||||
// Assert the global config directory exists (including child directories
|
||||
// such as repositories)
|
||||
assertConfigDir()
|
||||
|
||||
// Instantiate client with static defaults.
|
||||
c := &Client{
|
||||
repositories: &Repositories{},
|
||||
templates: &Templates{},
|
||||
builder: &noopBuilder{output: os.Stdout},
|
||||
pusher: &noopPusher{output: os.Stdout},
|
||||
deployer: &noopDeployer{output: os.Stdout},
|
||||
|
@ -178,54 +180,61 @@ func New(options ...Option) *Client {
|
|||
dnsProvider: &noopDNSProvider{output: os.Stdout},
|
||||
progressListener: &NoopProgressListener{},
|
||||
emitter: &noopEmitter{},
|
||||
repositoriesPath: filepath.Join(ConfigPath(), "repositories"),
|
||||
}
|
||||
c.repositories = newRepositories(c)
|
||||
c.templates = newTemplates(c)
|
||||
for _, o := range options {
|
||||
o(c)
|
||||
}
|
||||
// Initialize sub-managers using now-fully-initialized client.
|
||||
c.repositories = newRepositories(c)
|
||||
c.templates = newTemplates(c)
|
||||
|
||||
// Trigger the creation of the config and repository paths
|
||||
_ = ConfigPath() // Config is package-global scoped
|
||||
_ = c.RepositoriesPath() // Repositories is Client-specific
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// assertConfigDir makes a best-effort attempt to create the config directory
|
||||
// (including required subdirectories).
|
||||
func assertConfigDir() {
|
||||
// NOTE: regarding running as a user with no home directory:
|
||||
// The default is .config/func in current working directory when there is no
|
||||
// available HOME in which to find .`~/.config/func`.
|
||||
// Since it is expected that the code elsewhere never assume the config
|
||||
// directory exists (doing so is a racing condition), it is valid to simply
|
||||
// handle errors at this level.
|
||||
if err := os.MkdirAll(RepositoriesPath(), 0700); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating '%v': %v", RepositoriesPath(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// RepositoriesPath is the static default path to repositories used by a Client.
|
||||
// This path can be overridden on intantiation of a client using the
|
||||
// WithRepositories option.
|
||||
func RepositoriesPath() string {
|
||||
return filepath.Join(ConfigPath(), "repositories")
|
||||
}
|
||||
|
||||
// ConfigPath returns the default config directory path used by the Client.
|
||||
// The default is ~/.config/func. If defined, XDG_CONFIG_HOME is used.
|
||||
// In the event of an error calculating ~ (no home directory for the current
|
||||
// user), the relative path '.config/func' is used.
|
||||
func ConfigPath() string {
|
||||
// 'XDG_CONFIG_HOME/func' takes precidence if defined
|
||||
if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
|
||||
return filepath.Join(xdg, "func")
|
||||
}
|
||||
// The default config path is evaluated in the following order, from lowest
|
||||
// to highest precedence.
|
||||
// 1. The static default is DefaultConfigPath (./.config/func)
|
||||
// 2. ~/.config/func if it exists (can be expanded: user has a home dir)
|
||||
// 3. The value of $XDG_CONFIG_PATH/func if the environment variable exists.
|
||||
// The path will be created if it does not already exist.
|
||||
func ConfigPath() (path string) {
|
||||
path = DefaultConfigPath
|
||||
|
||||
// ~/.config/func is the default if ~ can be expanded
|
||||
if home, err := homedir.Expand("~"); err == nil {
|
||||
return filepath.Join(home, ".config", "func")
|
||||
path = filepath.Join(home, ".config", "func")
|
||||
}
|
||||
|
||||
// The default (edge case) is to return a relative path of .config inidicating
|
||||
// the current working directory when neither aforementioned exist.
|
||||
return ".config/func"
|
||||
// 'XDG_CONFIG_HOME/func' takes precidence if defined
|
||||
if xdg := os.Getenv("XDG_CONFIG_HOME"); xdg != "" {
|
||||
path = filepath.Join(xdg, "func")
|
||||
}
|
||||
|
||||
mkdir(path) // make sure it exists
|
||||
return
|
||||
}
|
||||
|
||||
// RepositoriesPath accesses the currently effective repositories path,
|
||||
// which defaults to [ConfigPath]/repositories but can be set explicitly using
|
||||
// the WithRepositories option when creating the client..
|
||||
// The path will be created if it does not already exist.
|
||||
func (c *Client) RepositoriesPath() (path string) {
|
||||
path = c.repositories.Path()
|
||||
mkdir(path) // make sure it exists
|
||||
return
|
||||
}
|
||||
|
||||
// RepositoriesPath is a convenience method for accessing the default path to
|
||||
// repositories that will be used by new instances of a Client unless options
|
||||
// such as WithRepositories are used to override.
|
||||
// The path will be created if it does not already exist.
|
||||
func RepositoriesPath() string {
|
||||
return New().RepositoriesPath()
|
||||
}
|
||||
|
||||
// OPTIONS
|
||||
|
@ -308,21 +317,21 @@ func WithDNSProvider(provider DNSProvider) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithRepositories sets the location to use for extensible template repositories.
|
||||
// Extensible template repositories are additional templates that exist on disk and are
|
||||
// not built into the binary.
|
||||
// WithRepositories sets the location to use for extensible template
|
||||
// repositories. Extensible template repositories are additional templates
|
||||
// that exist on disk and are not built into the binary.
|
||||
func WithRepositories(path string) Option {
|
||||
return func(c *Client) {
|
||||
c.Repositories().SetPath(path)
|
||||
c.repositoriesPath = path
|
||||
}
|
||||
}
|
||||
|
||||
// WithRepository sets a specific URL to a Git repository from which to pull templates.
|
||||
// This setting's existence precldes the use of either the inbuilt templates or any
|
||||
// repositories from the extensible repositories path.
|
||||
// WithRepository sets a specific URL to a Git repository from which to pull
|
||||
// templates. This setting's existence precldes the use of either the inbuilt
|
||||
// templates or any repositories from the extensible repositories path.
|
||||
func WithRepository(uri string) Option {
|
||||
return func(c *Client) {
|
||||
c.Repositories().SetRemote(uri)
|
||||
c.repositoriesURI = uri
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,3 +793,14 @@ func (p *NoopProgressListener) Increment(m string) {}
|
|||
func (p *NoopProgressListener) Complete(m string) {}
|
||||
func (p *NoopProgressListener) Stopping() {}
|
||||
func (p *NoopProgressListener) Done() {}
|
||||
|
||||
// mkdir attempts to mkdir, writing any errors to stderr.
|
||||
func mkdir(path string) {
|
||||
// Since it is expected that the code elsewhere never assume directories
|
||||
// exist (doing so is a racing condition), it is valid to simply
|
||||
// handle errors at this level.
|
||||
if err := os.MkdirAll(path, 0700); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating '%v': %v", path, err)
|
||||
debug.PrintStack()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,7 +161,6 @@ func TestRemoteRepositories(t *testing.T) {
|
|||
client := fn.New(
|
||||
fn.WithRegistry(DefaultRegistry),
|
||||
fn.WithRepository("https://github.com/boson-project/test-templates"),
|
||||
fn.WithRepositories("testdata/repositories"),
|
||||
)
|
||||
err := client.Create(fn.Function{
|
||||
Root: ".",
|
||||
|
|
|
@ -246,7 +246,7 @@ func newCreateConfig(args []string, clientFn createClientFn) (cfg createConfig,
|
|||
// it is still available as an environment variable.
|
||||
repositories = os.Getenv("FUNC_REPOSITORIES")
|
||||
if repositories == "" { // if no env var provided
|
||||
repositories = fn.RepositoriesPath() // use ~/.config/func/repositories
|
||||
repositories = fn.New().RepositoriesPath() // use ~/.config/func/repositories
|
||||
}
|
||||
|
||||
// Config is the final default values based off the execution context.
|
||||
|
|
|
@ -90,10 +90,6 @@ func NewCredentialsProvider(
|
|||
}
|
||||
}
|
||||
|
||||
// Creating an instance of the fn.Client ensures that the config path
|
||||
// exists:
|
||||
_ = fn.New()
|
||||
|
||||
authFilePath := filepath.Join(fn.ConfigPath(), "auth.json")
|
||||
sys := &containersTypes.SystemContext{
|
||||
AuthFilePath: authFilePath,
|
||||
|
|
|
@ -31,9 +31,16 @@ type Repositories struct {
|
|||
path string
|
||||
|
||||
// Optional uri of a single repo to use in leau of embedded and extensible.
|
||||
// Enables single-repository mode. This replaces the default embedded repo
|
||||
// and extended repositories. This is an important mode for both diskless
|
||||
// (config-less) operation, such as security-restrited environments, and for
|
||||
// running as a library in which case environmental settings should be
|
||||
// ignored in favor of a more functional approach in which only inputs affect
|
||||
// outputs.
|
||||
remote string
|
||||
|
||||
// backreference to the client enabling full api access for the repo manager
|
||||
// backreference to the client enabling this repositorires manager to
|
||||
// have full API access.
|
||||
client *Client
|
||||
}
|
||||
|
||||
|
@ -42,32 +49,17 @@ type Repositories struct {
|
|||
// full client API during implementations.
|
||||
func newRepositories(client *Client) *Repositories {
|
||||
return &Repositories{
|
||||
path: DefaultRepositoriesPath,
|
||||
client: client,
|
||||
path: client.repositoriesPath,
|
||||
remote: client.repositoriesURI,
|
||||
}
|
||||
}
|
||||
|
||||
// SetPath to repositories under management.
|
||||
func (r *Repositories) SetPath(path string) {
|
||||
r.path = path
|
||||
}
|
||||
|
||||
// Path returns the currently active repositories path under management.
|
||||
func (r *Repositories) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
// SetRemote enables single-repository mode.
|
||||
// Enables single-repository mode. This replaces the default embedded repo
|
||||
// and extended repositories. This is an important mode for both diskless
|
||||
// (config-less) operation, such as security-restrited environments, and for
|
||||
// running as a library in which case environmental settings should be
|
||||
// ignored in favor of a more functional approach in which only inputs affect
|
||||
// outputs.
|
||||
func (r *Repositories) SetRemote(uri string) {
|
||||
r.remote = uri
|
||||
}
|
||||
|
||||
// List all repositories the current configuration of the repo manager has
|
||||
// defined.
|
||||
func (r *Repositories) List() ([]string, error) {
|
||||
|
|
Loading…
Reference in New Issue