app

package
v3.0.0-alpha.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: BSD-2-Clause Imports: 19 Imported by: 0

Documentation

Overview

Package app resolves what an instance command acts on: the application a target names, the directory it lives in, the pair of instances description and cluster config it runs from, and the instances the target selects. It takes over from cli/running the part that took the directory of tt.yaml for the application and its name for the application's name.

It finds and reads; it does not run anything. Where the data, the logs and the sockets of an instance go, and which tarantool runs it, are built on top of its result.

Targets

An instance command takes app:inst, app:* or no target, and lists of the first two separated by commas or given as several arguments (ParseTargets). This is the target grammar every namespace shares, read the same way in each. A bare app is a cluster-level target there, so an instance command refuses it with a hint rather than taking it for app:*: forgetting an instance name must not stop every instance. There is no :inst short form; an empty application name is kept free for one, should it be needed.

Scopes

An application lives in one of three scopes, and --scope picks one outright. Without it, app:... is the project the command runs in when that project is called app, and the system scope otherwise; the user scope is reached through --scope only. No target is the project the command runs in, and there has to be one. A name missing from the chosen scope is never looked for in the others: what a command acts on must not depend on what happens to be installed where.

The project is the nearest directory, up from the -C directory or the working directory, that holds a project marker (scope.FindProject). It is named by [package].name of its manifest, or after its directory when it has no manifest. A manifest that does not parse is an error, not a reason to try the system scope: the project could be the application meant.

In the user and system scopes an application is <root>/apps/<app>, a link to its active version, and the link is resolved at once: the version directory is the application directory. The instances get it as their working directory and the description's paths point into it, so an instance keeps running the version it started from when the link moves.

A caller may pin the application directory instead (Options.Dir): the watchdog of a running instance does, so that restarting it after an exit stays on the version it started from, while the pair is read anew.

Pairs

An application runs from a pair: instances.yaml, what runs on this host, and config.yaml, the file tarantool -c gets. They must agree on the names of the Tarantool instances, and different templates describe different topologies, so a pair is taken whole from one directory and never put together from two.

In the user and system scopes the pair of the host, <root>/etc/<app>/, wins when it is there; otherwise, and always in a project, the pair is the template .templates/<template>/ of the application, "default" unless --template names another. The pair of the host lives outside of the version, which is immutable, so that it survives switching versions. A broken pair is an error, never a reason to fall back to the template, which would start a topology the host was not given; and --template is refused when the host has a pair.

The config is loaded by cluster.GetClusterConfig, so a config holding only config.etcd or config.storage brings in the rest of the cluster config from the storage, as for every other command. Every record without binary or script is a Tarantool instance configured by that config, and must be in its topology; the topology may hold more instances, which run on other hosts. Records with script or binary are not checked against it.

The record "*" stands for every instance of the topology that no other record names, as a Tarantool instance configured by the config with the fields of "*"; its instances take its place in the description, in the order of their names (App.Instances). A record naming an instance of the topology replaces "*" for it whole. A template of config.yaml without a description runs its whole topology, as the description "*": {} would: an instance added to the config is not left out because it was forgotten in the description. The pair of a host needs its description, "*" included: it says what runs on that host, while the rest of a cluster may run on others. The topology is free to lose an instance "*" stands for: a cluster config published into the pair is checked against the other records alone (App.CheckTopology).

Only the Tarantool instances of the config, and "*", need the config. A description of instances that run their scripts and of external processes alone makes a pair without config.yaml, and a config.yaml next to it is not read. A description that needs the config without config.yaml next to it is an incomplete pair.

The commands that act on the processes of the instances read the config as far as they can (Options.BestEffortConfig). When they cannot, "*" stands for the instances that have run in the scope of the application, those with a directory in <root>/var/run/<app>/: an etcd that is down does not keep them from being stopped. When they can, "*" stands as well for the instances the topology lacks whose directory there still holds the pid file of the watchdog, tt.pid: an instance dropped from the topology keeps running, and is still there to be stopped and looked at, as the layout gives it. The directory alone, which outlives a stopped instance, brings back none.

A Tarantool instance, configured by the cluster config or by its script, runs in the application directory with the command line tt makes up, so workdir and args in its record are refused rather than silently ignored. They are fields of external processes.

Errors

Every error carries sdk.ExitFailure: the fix is on the user's side, and a failure of the system underneath, such as a refused read, still shows through what it wraps. The failures a caller may want to tell apart wrap the Err* values. When the command runs where there is no project but tt2 left its files, tt.yaml or instances.yml, the error says so and points to the migration guide; the files are only looked for, never read.

Index

Constants

View Source
const (
	// AllInstances is the instance selector of app:*, every instance the
	// pair of the application describes.
	AllInstances = "*"
)
View Source
const (
	// DefaultTemplate is the template a command runs without --template.
	DefaultTemplate = "default"
)

Variables

View Source
var (
	// ErrInvalidTarget is a target outside the grammar of instance-level
	// commands, such as a bare application name, or a name that cannot be
	// an application or an instance.
	ErrInvalidTarget = errors.New("invalid target")
	// ErrUnknownScope is a --scope value other than project, user and
	// system.
	ErrUnknownScope = errors.New("unknown scope")
	// ErrInvalidTemplate is a --template value that cannot name a template
	// directory.
	ErrInvalidTemplate = errors.New("invalid template name")
	// ErrNoProject is a command that needs the project it runs in, without a
	// target or with --scope project, run outside of any project.
	ErrNoProject = errors.New("not inside a project")
	// ErrTT2Environment is wrapped, next to ErrNoProject or ErrAppNotFound,
	// when the directory the command runs in is a tt2 environment.
	ErrTT2Environment = errors.New("this is a tt2 environment")
	// ErrInvalidManifest is a project manifest that does not parse or names
	// no usable application.
	ErrInvalidManifest = errors.New("invalid project manifest")
	// ErrAppNotFound is an application missing from the scope it was looked
	// for in.
	ErrAppNotFound = errors.New("application not found")
	// ErrNoPair is an application without the pair it is to run from:
	// neither file of it is there.
	ErrNoPair = errors.New("no instances description")
	// ErrIncompletePair is a pair whose description needs the cluster
	// config it lacks, for a Tarantool instance configured by it or for
	// "*", or the pair of a host without its description.
	ErrIncompletePair = errors.New("incomplete pair")
	// ErrTemplateConflict is --template given for an application that runs
	// from the pair of the host.
	ErrTemplateConflict = errors.New("--template conflicts with the host pair")
	// ErrUnsupportedField is a field of an instance record that an instance
	// of its kind cannot honour.
	ErrUnsupportedField = errors.New("unsupported field")
	// ErrTopologyMismatch is a Tarantool instance of the description that the
	// cluster config does not declare.
	ErrTopologyMismatch = errors.New("instances missing from the cluster topology")
	// ErrTopologyConflict is an instance of the description that runs its
	// script or is an external process, named as an instance of the cluster
	// config.
	ErrTopologyConflict = errors.New("records taking names of the cluster topology")
	// ErrInstanceNotFound is a target naming an instance the description
	// does not declare.
	ErrInstanceNotFound = errors.New("instance not found")
	// ErrNoInstances is a target that selects every instance of a
	// description that declares none, "*" standing for none either.
	ErrNoInstances = errors.New("no instances")
)

The failures the resolver tells apart. An error about a target, a flag, a missing application or instance, or a pair that does not fit together wraps one of them, so that a caller matches it with errors.Is rather than by the message, which names the paths and the names involved. A description that does not parse wraps instances.ErrInvalid instead, and a file or a config storage that cannot be read wraps what failed.

Functions

func CheckTemplate

func CheckTemplate(ctx context.Context, desc instances.Description, descFile, configFile string,
) (bool, error)

CheckTemplate checks a template the way Load checks a pair, for tt package pack: desc, the description descFile, against the topology of the cluster config file configFile, when that file holds the cluster config itself. A config that points to a config storage (config.etcd, config.storage) is left unread, and checked reports so: the storage may be out of reach where the package is packed, and the instances check it when they start. The file alone is read, without the TT_ environment of the packing host, since the template goes into the package as it is.

func HostDir

func HostDir(root, name string) string

HostDir returns the directory of the host files of the application name installed under root, the root of the user or the system scope: <root>/etc/<app>/. It holds the pair of the host, and the files tt keeps for the application on the host, such as the mark of its systemd units.

func Names

func Names(opts Options) []string

Names returns the names of the applications a target may name with opts, for the completion of a target: the project the command runs in, unless opts.Scope is user or system, and then the applications installed in the system scope, or in the scope opts.Scope names, in the order of their names. A name comes once, the project's first.

These are the applications Load finds without --scope, or with the scope opts.Scope names; the user scope is offered only for --scope user, as Load looks there only then. Names finds what it can and reports nothing: a project whose manifest does not parse, or a scope root that cannot be read, offers no name.

Types

type App

type App struct {
	// Name is the name of the application: [package].name of the project's
	// manifest or the name of the project directory, and the directory name
	// under <root>/apps/ in the user and system scopes.
	Name string
	// Scope is the scope the application was found in.
	Scope Scope
	// Root is the root of that scope: .tarantool in the project directory,
	// or the user or the system root.
	Root string
	// Dir is the application directory: the project directory, or the
	// version directory <root>/apps/<app> pointed to when it was resolved.
	// It is the working directory of the Tarantool instances, and the
	// relative paths of the description are resolved against it.
	Dir string
	// Pair is the pair the application runs from.
	Pair Pair
	// Config is the cluster config of the pair, with what a config storage
	// it names holds merged in. It is nil when the description has no
	// Tarantool instance configured by it (HasClusterInstances): the config
	// is not read then, whether the pair has one or not. With
	// Options.BestEffortConfig it is nil as well when it, or the topology in
	// it, cannot be read.
	Config *goconfig.MutableConfig
	// BestEffortConfig is Options.BestEffortConfig the application was
	// loaded with: Config may lack an instance of the description, or hold
	// what does not fit it, and the instance is then taken as the layout
	// gives it.
	BestEffortConfig bool
	// ConfigError is why the config of the pair could not be read, with
	// Options.BestEffortConfig; nil when it was read or not needed.
	ConfigError error
	// Instances are every instance of the description, in its order: its
	// records, and the instances "*" stands for in place of it.
	Instances []instances.Instance
	// Integrity are the integrity checks of the application,
	// Options.IntegrityFor's for it; nil when the application was loaded
	// without them, as it is while the checks are off.
	Integrity *integrity.IntegrityCtx
	// contains filtered or unexported fields
}

App is an application found and loaded with its pair.

func Load

func Load(ctx context.Context, name string, opts Options) (*App, error)

Load finds the application name names, chooses its pair, and loads and checks the pair. An empty name is the project the command runs in; it is an error outside of a project and with --scope user or system.

Load checks the pair as a whole, whichever instances a command then acts on: a Tarantool instance the topology does not declare, or a field an instance cannot honour, breaks every command on the application alike, but for those that read the config as far as they can (Options.BestEffortConfig), which do not check the topology.

func (*App) CheckTopology

func (app *App) CheckTopology(topology []string, config string) error

CheckTopology checks the description of app against topology, the instances of a cluster config about to take the place of the config of its pair, as tt cluster publish publishes one; config names that config for a message. Every Tarantool instance of the cluster config the description names must stay in it, and no instance that runs its script and no external process may take a name of it. A config that leaves out an instance the description names would leave the pair broken.

The instances "*" stands for are not checked: "*" follows the topology, whichever instances the config gives it, and every project tt create makes has it, so checking them would keep a project from ever dropping an instance by publishing. One dropped while it runs keeps running without its config; the commands that act on the processes of the instances still find it by its pid file (Options.BestEffortConfig), so that it can be stopped.

func (*App) HasClusterInstances

func (app *App) HasClusterInstances() bool

HasClusterInstances reports whether the description of app has Tarantool instances configured by the cluster config, rather than only instances that run their scripts and external processes. "*" counts, whatever instances it stands for: it is the topology of the cluster config.

type Options

type Options struct {
	// Scope is the --scope value; empty leaves the choice to the rules of
	// Load.
	Scope Scope
	// Template is the --template value; empty when it was not given, which
	// selects DefaultTemplate when the application runs from a template.
	Template string
	// Start is the directory the search for the project starts from: the
	// -C directory, or the working directory when it is empty.
	Start string
	// Dir pins the application directory: the application is loaded from
	// Dir, an absolute path, rather than looked for, and Scope must name
	// the scope Dir belongs to. Empty finds the application as usual.
	//
	// It is how a running instance gets back to its application, the
	// project or the very version it started from, while the pair is still
	// chosen and read anew. Dir applies to every application the targets of
	// Resolve name, so it goes with targets naming one.
	Dir string
	// Integrity checks the files the resolver reads and the config storage
	// the cluster config may point to. A context without a repository checks
	// nothing.
	Integrity integrity.IntegrityCtx
	// IntegrityFor, when set, gives the integrity checks of the application
	// found in dir in the scope: Load reads its pair through them, rather
	// than through Integrity, which then checks only what finding the
	// application reads, and hands them on as App.Integrity. tt sets it when
	// the checks are on, rooting them at the application - a project or an
	// installed version - and at the runtimes it runs on, whatever the
	// directory tt runs in.
	IntegrityFor func(scope Scope, dir string) (integrity.IntegrityCtx, error)
	// BestEffortConfig reads the cluster config of the pair, and the config
	// storage it names, as far as it can be read: a config that is missing,
	// broken or out of reach leaves App.Config nil and App.ConfigError set,
	// rather than failing the load, and the description is not checked
	// against the topology. The description is read and checked as always;
	// its "*" stands, without the config, for the instances that have run in
	// the scope, those with a directory in <root>/var/run/<app>/. With the
	// config, it stands for the topology, and for the instances whose
	// directory there holds the pid file of the watchdog, tt.pid, as well:
	// an instance dropped from the topology while it runs is still found,
	// without the config, to be stopped.
	//
	// It is for the commands that act on the processes of the instances,
	// which find them by the description and their pid files, such as
	// tt instance stop and status: an etcd that is down or a config broken
	// by an edit must not keep a running instance from being stopped or
	// looked at, as it did not in tt2; and a config that can be read still
	// says where the instance keeps what it moves out of the layout, such
	// as its console socket. A command that needs the config to act, such
	// as tt instance start, requires it.
	//
	// tt cluster publish into the pair reads it so as well: the config it
	// writes takes the place of the one that cannot be read, an empty
	// storage before the first publication among them, and is checked
	// against the description instead (App.CheckTopology).
	BestEffortConfig bool
}

Options are what a command adds to its target to find the application.

type Pair

type Pair struct {
	// Template is the name of the template the pair is, or empty for the
	// pair of the host, <root>/etc/<app>/.
	Template string
	// Dir is the directory holding the pair.
	Dir string
	// InstancesFile is the path of the instances description, empty for a
	// template without one.
	InstancesFile string
	// ConfigFile is the path of the cluster config, the file tarantool -c
	// gets; empty when the pair has none.
	ConfigFile string
}

Pair is the instances description and the cluster config an application runs from. The two agree on the names of the Tarantool instances, and different templates describe different topologies, so a pair is taken whole from one directory.

The cluster config is needed only by the Tarantool instances it configures: a description of instances that run their scripts and of external processes alone makes a pair without one. A template of the config alone runs every instance of its topology, as the description "*": {} would; the pair of a host needs its description, which says what runs on that host.

func (*Pair) DescriptionFile

func (pair *Pair) DescriptionFile() string

DescriptionFile names, for a message, the file the instances of the pair are described by: the instances description, or the cluster config of a template without one, whose topology is all the description it has.

func (*Pair) Host

func (pair *Pair) Host() bool

Host reports whether the pair is the pair of the host rather than a template.

func (*Pair) String

func (pair *Pair) String() string

String names the pair for a message.

type Scope

type Scope string

Scope is where an application is looked for. The values are those of --scope, shared with tt package and tt binary.

const (
	// ScopeProject is the project the command runs in: the nearest directory
	// up from the start directory with a project marker.
	ScopeProject Scope = "project"
	// ScopeUser is an application installed under the user root.
	ScopeUser Scope = "user"
	// ScopeSystem is an application installed under the system root.
	ScopeSystem Scope = "system"
)

func ParseScope

func ParseScope(value string) (Scope, error)

ParseScope parses a --scope value. The empty value is the empty Scope, which leaves the choice to the rules of Load.

func ScopeOf

func ScopeOf(name string, opts Options) (Scope, string, error)

ScopeOf returns the scope Load looks for the application name in with opts, and the root of that scope, without looking for the application in it: --scope when it is given; otherwise the project the command runs in when the project has that name, and the system scope when it does not.

It is for a command that acts on what an application has left in its scope rather than on the application, such as the systemd units of one that may be gone: the target names the same scope there as it does in the instance commands. A project, in the project scope or found by the rules, is read for its name, as Load reads it.

func (Scope) String

func (scope Scope) String() string

String returns the scope as --scope spells it.

type Selection

type Selection struct {
	// App is the application, with every record of its description.
	App *App
	// Instances are the records the targets select, in the order of the
	// description: the order instances start in, whatever the order of the
	// targets.
	Instances []instances.Instance
}

Selection is the instances a command acts on in one application.

func Resolve

func Resolve(ctx context.Context, targets []Target, opts Options) ([]Selection, error)

Resolve finds the applications targets name and the instances they select: one Selection per application, in the order the targets first name them. No targets select every instance of the project the command runs in.

app:* and no target select every instance of the description, which is what runs on this host: the whole topology of the cluster only when "*" stands for it. Selecting none is an error, since a command that does nothing while reporting success hides a mistake. app:inst must name an instance of the description: one only the topology knows runs on another host. A target given twice selects its instance once.

type Target

type Target struct {
	// App is the name of the application. It is never empty: the empty name
	// before the delimiter is kept for a short form naming the current
	// project, should one be needed.
	App string
	// Instance is the name of the instance, or AllInstances.
	Instance string
}

Target is one instance-level target: an instance of an application, or every instance of it.

func ParseTargets

func ParseTargets(args []string) ([]Target, error)

ParseTargets parses the targets of an instance-level command: app:inst, app:* and comma-separated lists of them, in one argument or several. No arguments is no target, and that is a valid target: every instance of the project the command runs in. The result keeps the order of the arguments.

A bare application name is an error with a hint rather than app:*: in the grammar shared by every namespace a bare name is a cluster-level target, and an instance command must not act on every instance because an instance name was left out.

func (Target) String

func (target Target) String() string

String returns the target as it is written on the command line.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL