Documentation
¶
Overview ¶
@Author: LinkLeong link@icewhale.com
*@Date: 2022-07-12 09:48:56 *@LastEditors: LinkLeong *@LastEditTime: 2022-09-02 22:10:05 *@FilePath: /CasaOS/service/service.go *@Description: *@Website: https://www.casaos.io *Copyright (c) 2022 by icewhale, All Rights Reserved.
Index ¶
- Constants
- Variables
- func ApiService() (api.Compose, client.APIClient, error)
- func ApplyDataPath(root string) string
- func ApplyGitAppsDataRoot(v string)
- func ApplyLogsSettings(retentionDays, maxSizeMB int, includeOthers bool) error
- func ApplyRetention(ctx context.Context, destination, app string, keep int, runner retentionRunner)
- func Begin(app, kind string) (end func(), err error)
- func BrowseBackup(ctx context.Context, browser BackupBrowser, ...) ([]rclone.File, error)
- func BuildCatalog(storeRoot string) (map[string]*ComposeApp, error)
- func CheckGitApps(ctx context.Context)
- func CheckImageUpdates(ctx context.Context) (*codegen.ImageUpdateCheckResult, error)
- func CheckImageUpdatesForApp(ctx context.Context, composeApp *ComposeApp) error
- func CheckRestorePaths(paths []string) error
- func CurrentUpdateAllRun() *codegen.ComposeAppUpdateRun
- func DanglingImages(ctx context.Context) (*codegen.DanglingImages, error)
- func DeleteGitApp(ctx context.Context, name string) error
- func FollowedContainers() map[string]bool
- func GenerateYAMLFromComposeApp(compose ComposeApp) ([]byte, error)
- func GitAppGrid(app string) *codegen.GitAppGrid
- func GitAppNames() ([]string, error)
- func GitAppsWithoutContainers(listed []string) []codegen.WebAppGridItem
- func ImageUpdateAvailable(appName string) *bool
- func IsDue(schedule BackupSchedule, now time.Time) (bool, error)
- func LoadCategoryMap(storeRoot string) map[string]codegen.CategoryInfo
- func LoadImageUpdates()
- func LoadRecommend(storeRoot string) []string
- func Logs() *logstore.Store
- func NewResticChecker() resticChecker
- func NewScheduledBackupRunner() *scheduledBackupRunner
- func ParseEnvFile(text []byte) (map[string]string, error)
- func PlanUpdateAll(apps map[string]*ComposeApp, available func(*ComposeApp) bool, ...) []codegen.ComposeAppUpdatePlanApp
- func PruneDanglingImages(ctx context.Context) (*codegen.DanglingImages, error)
- func PublishEventWrapper(ctx context.Context, eventType message_bus.EventType, ...)
- func ReceiveGitWebhook(app string, header http.Header, body io.Reader) (string, error)
- func RecordAppEvent(app, kind, fromImage, toImage, detail string)
- func RecordBackupRun(record BackupRunRecord) error
- func RecoverGitApps(ctx context.Context)
- func RecoverHeldApps(docker containerStopStarter)
- func ResticChecks() (map[string]ResticCheck, error)
- func RootFor(app, stamp string) string
- func RunDueBackups(ctx context.Context, now time.Time, runner backupRunner)
- func RunDueResticChecks(ctx context.Context, now time.Time, checker resticChecker)
- func RunsToPurge(stamps []string, keep int) []string
- func SaveBackupSchedules(schedules []BackupSchedule) error
- func SetLogs(s *logstore.Store)
- func SetStats(s *dockerstats.Sampler)
- func SplitInstallEnv(text []byte) ([]byte, []byte, error)
- func Standardize(text string) string
- func StartLogs(ctx context.Context, docker logsDocker) error
- func StartResticCheck(ctx context.Context, now time.Time, checker resticChecker, destination string) (<-chan struct{}, bool)
- func StartStats(ctx context.Context) error
- func StartUpdateAll(ids []string, ...) (*codegen.ComposeAppUpdateRun, error)
- func Stats() *dockerstats.Sampler
- func StopLogs()
- func StopStats()
- func StoreRoot(workdir string) (string, error)
- func Systemd() unitControl
- func UpdateOneForRun(ctx context.Context, id string) (codegen.ComposeAppUpdateRunAppState, string)
- func VolumesToRemove(volumes []ContainerVolume, requested []string) (remove []string, refused map[string]string)
- func WatchDocker(ctx context.Context)
- type App
- type AppOperation
- type AppStore
- type AppStoreManagement
- func (a *AppStoreManagement) AppStoreList() []codegen.AppStoreMetadata
- func (a *AppStoreManagement) AppStoreMap() (map[string]AppStore, error)
- func (a *AppStoreManagement) Catalog() (map[string]*ComposeApp, error)
- func (a *AppStoreManagement) CategoryMap() (map[string]codegen.CategoryInfo, error)
- func (a *AppStoreManagement) ChangeGlobal(key string, value string) error
- func (a *AppStoreManagement) ComposeApp(id string) (*ComposeApp, error)
- func (a *AppStoreManagement) DeleteGlobal(key string) error
- func (a *AppStoreManagement) FinishUpgrade(appID string)
- func (a *AppStoreManagement) ForgetUpgradable(appID string)
- func (a *AppStoreManagement) IsUpdateAvailable(composeApp *ComposeApp) bool
- func (a *AppStoreManagement) IsUpdateAvailableWith(composeApp *ComposeApp, storeComposeApp *ComposeApp) (bool, string, error)
- func (a *AppStoreManagement) IsUpdating(appID string) bool
- func (a *AppStoreManagement) OnAppStoreRegister(fn func(string) error)
- func (a *AppStoreManagement) OnAppStoreUnregister(fn func(string) error)
- func (a *AppStoreManagement) Recommend() ([]string, error)
- func (a *AppStoreManagement) RegisterAppStore(ctx context.Context, appstoreURL string, ...) error
- func (a *AppStoreManagement) RegisterAppStoreSync(ctx context.Context, appstoreURL string, ...) error
- func (a *AppStoreManagement) StartUpgrade(appID string)
- func (a *AppStoreManagement) UnregisterAppStore(appStoreID uint) error
- func (a *AppStoreManagement) UpdateAvailability(composeApp *ComposeApp) (bool, string)
- func (a *AppStoreManagement) UpdateCatalog() error
- func (a *AppStoreManagement) WorkDir() (string, error)
- type BackupBrowser
- type BackupCopier
- type BackupEntry
- type BackupGit
- type BackupHeldApp
- type BackupKind
- type BackupManifest
- func BackupOnDemand(ctx context.Context, app *ComposeApp, docker backupDocker, copier BackupCopier, ...) (BackupManifest, error)
- func PlanBackup(app string, inventory []BackupEntry, volumeMountpoints map[string]string) BackupManifest
- func PlanSystemBackup(exists func(string) bool) BackupManifest
- func RunBackup(ctx context.Context, app *ComposeApp, docker backupDocker, copier BackupCopier, ...) (BackupManifest, error)
- func RunSystemBackup(ctx context.Context, copier BackupCopier, units unitControl, ...) (BackupManifest, error)
- func SystemBackupOnDemand(ctx context.Context, copier BackupCopier, units unitControl, ...) (BackupManifest, error)
- type BackupOperation
- type BackupOptions
- type BackupPartialRestorer
- type BackupRestorer
- type BackupRunRecord
- type BackupSchedule
- type ComposeApp
- func ComposeAppFromSettingsYAML(buf []byte, keep map[string]struct{}) (*ComposeApp, error)
- func LoadComposeAppForEditing(appID, configFile string, keep map[string]struct{}) (*ComposeApp, error)
- func LoadComposeAppFromConfigFile(appID string, configFile string) (*ComposeApp, error)
- func NewComposeAppFromYAML(yaml []byte, skipInterpolation, skipValidation bool) (*ComposeApp, error)
- func NewComposeAppWithEnv(text, env []byte) (resolved, kept *ComposeApp, err error)
- func (a *ComposeApp) App(name string) *App
- func (a *ComposeApp) Apply(ctx context.Context, newComposeYAML []byte) error
- func (a *ComposeApp) ApplyEnv(ctx context.Context, env []byte) error
- func (a *ComposeApp) Apps() map[string]*App
- func (a *ComposeApp) AuthorType() codegen.StoreAppAuthorType
- func (a *ComposeApp) BackupInventory() []BackupEntry
- func (a *ComposeApp) Containers(ctx context.Context) (map[string][]api.ContainerSummary, error)
- func (a *ComposeApp) Create(ctx context.Context, options api.CreateOptions, service api.Compose) error
- func (a *ComposeApp) DockerVolumeName(composeName string) string
- func (a *ComposeApp) EnvFile() string
- func (a *ComposeApp) EnvFileText() ([]byte, error)
- func (a *ComposeApp) EnvKeys() (map[string]struct{}, error)
- func (a *ComposeApp) GetPortsInUse() (*codegen.ComposeAppValidationErrorsPortsInUse, error)
- func (a *ComposeApp) GetPortsInUseFor(profiles []string) (*codegen.ComposeAppValidationErrorsPortsInUse, error)
- func (a *ComposeApp) HealthCheck() (bool, error)
- func (a *ComposeApp) IsUncontrolled() bool
- func (a *ComposeApp) Logs(ctx context.Context, lines int, services ...string) ([]byte, error)
- func (a *ComposeApp) MainService() (*App, error)
- func (a *ComposeApp) MainServiceName() string
- func (a *ComposeApp) MainTag() (string, error)
- func (a *ComposeApp) Pull(ctx context.Context) error
- func (a *ComposeApp) PullAndApply(ctx context.Context, newComposeYAML []byte) error
- func (a *ComposeApp) PullAndInstall(ctx context.Context) error
- func (a *ComposeApp) SetStatus(ctx context.Context, status codegen.RequestComposeAppStatus) error
- func (a *ComposeApp) SetStoreAppID(storeAppID string) (string, bool)
- func (a *ComposeApp) SetTitle(title, lang string)
- func (a *ComposeApp) SetUncontrolled(uncontrolled bool) error
- func (a *ComposeApp) StoreInfo(includeApps bool) (*codegen.ComposeAppStoreInfo, error)
- func (a *ComposeApp) SupportsArchitecture(arch string) bool
- func (a *ComposeApp) Uninstall(ctx context.Context, deleteConfigFolder bool) error
- func (a *ComposeApp) Up(ctx context.Context, service api.Compose) error
- func (a *ComposeApp) UpWithCheckRequire(ctx context.Context, service api.Compose) error
- func (a *ComposeApp) Update(ctx context.Context) error
- func (a *ComposeApp) UpdateEventPropertiesFromStoreInfo(eventProperties map[string]string) error
- func (a *ComposeApp) UpdateNow(ctx context.Context) error
- func (a *ComposeApp) WriteEnvFile(text []byte) error
- type ComposeService
- func (s *ComposeService) Install(ctx context.Context, composeApp *ComposeApp, env []byte) error
- func (s *ComposeService) IsInstalling(appName string) bool
- func (s *ComposeService) List(ctx context.Context) (map[string]*ComposeApp, error)
- func (s *ComposeService) PrepareWorkingDirectory(name string) (string, error)
- func (s *ComposeService) RecoverAppsWaitingForStorage(ctx context.Context)
- func (s *ComposeService) Status(ctx context.Context, appID string) (string, error)
- func (s *ComposeService) Uninstall(ctx context.Context, composeApp *ComposeApp, deleteConfigFolder bool) error
- type ContainerVolume
- type DockerService
- type ErrAppBusy
- type GitAppChanges
- type GitAppRegistration
- type GitAppView
- func CheckGitApp(ctx context.Context, name string) (*GitAppView, error)
- func CreateGitApp(ctx context.Context, registration GitAppRegistration) (*GitAppView, error)
- func DeployGitApp(ctx context.Context, name, commit string, env *string) (*GitAppView, error)
- func DeployGitAppTag(ctx context.Context, name, tag string, env *string) (*GitAppView, error)
- func GetGitApp(ctx context.Context, name string) (*GitAppView, error)
- func UpdateGitApp(ctx context.Context, name string, changes GitAppChanges) (*GitAppView, error)
- type GitCheckView
- type GitComposeServiceView
- type GitComposeView
- type GitDeployedView
- type GitHeadView
- type GitHistoryView
- type GitRequestError
- type GitTag
- type GitWebhookDelivery
- type GitWebhookView
- type LogsSettings
- type ProgressDetail
- type PullOut
- type ResticCheck
- type RestoreInstaller
- type RestoreOptions
- type RestoreReport
- func RestoreBackup(ctx context.Context, installed *ComposeApp, docker backupDocker, ...) (RestoreReport, error)
- func RestoreOnDemand(ctx context.Context, installed *ComposeApp, docker backupDocker, ...) (RestoreReport, error)
- func RestoreSystemBackup(ctx context.Context, restorer BackupRestorer, units unitControl, ...) (RestoreReport, error)
- func SystemRestoreOnDemand(ctx context.Context, restorer BackupRestorer, units unitControl, ...) (RestoreReport, error)
- type ScheduleEvery
- type Services
- type StatusType
- type SystemBackupOptions
- type SystemRestoreOptions
- type Throttler
Constants ¶
const ( GitWebhookChecking = "checking" GitWebhookCoalesced = "coalesced" GitWebhookQueued = "queued" // GitWebhookHeld is a push that started no check: an operating system upgrade is open. GitWebhookHeld = "held" GitWebhookIgnored = "ignored" GitWebhookPong = "pong" )
Answers a forge is given: 200 for GitWebhookPong, 202 for the others.
const BackupFormatVersion = 1
BackupFormatVersion is bumped when the layout changes in a way a reader must know about.
const GitComposeExample = `services:
app:
build: .
ports:
- "8080:8080"
restart: unless-stopped
`
GitComposeExample is what a repository without a compose file is told to add.
const ManifestFileName = "manifest.json"
ManifestFileName sits at the root of a backup.
const SystemBackupName = "casaos-system"
SystemBackupName is the app name the box's own backups are filed under.
Variables ¶
var ( ErrNotAppStore = fmt.Errorf("not an appstore") ErrDefaultAppStoreNotFound = fmt.Errorf("default appstore not found") )
var ( ErrBackupPathOutside = errors.New("a path in a backup stays inside it") ErrBackupPathNotHeld = errors.New("the backup holds nothing at that path") ErrRestoreBoxInParts = errors.New("the backup of the box is looked into and restored whole, not in parts") )
var ( ErrRestoreNeedsDestination = errors.New("a restore needs a destination") ErrRestoreNeedsApp = errors.New("a restore needs an app") ErrRestoreNeedsStamp = errors.New("a restore needs the backup to restore from") ErrRestorePathsNeedApp = errors.New("a restore of some files needs the app installed: install it, or restore the whole backup") )
var ( ErrComposeAppNotFound = fmt.Errorf("compose app not found") ErrComposeAppNotMatch = fmt.Errorf("compose app not match") ErrComposeExtensionNameXCasaOSNotFound = fmt.Errorf("extension `%s` not found", common.ComposeExtensionNameXCasaOS) ErrComposeFileNotFound = fmt.Errorf("compose file not found") ErrInvalidComposeAppStatus = fmt.Errorf("invalid compose app status") ErrMainAppNotFound = fmt.Errorf("main app not found") ErrNotFoundInAppStore = fmt.Errorf("not found in app store") ErrSetStoreAppID = fmt.Errorf("failed to set store app ID") ErrStoreInfoNotFound = fmt.Errorf("store info not found") ErrMainServiceNotSpecified = fmt.Errorf("main service not been specified") )
var ( // ErrGitAppNameTaken is answered 409. ErrGitAppNameTaken = errors.New("the name is already used by an app, a compose project or an app deployed from git") // ErrGitAppDeployed is answered 409: a deployed app goes with its uninstall. ErrGitAppDeployed = errors.New("the app has been deployed: uninstall it instead") )
var ( // ErrGitWebhookNotFound is an unknown app and a webhook turned off alike, so that the // route does not tell which apps exist. ErrGitWebhookNotFound = errors.New("not found") ErrGitWebhookSignature = errors.New("invalid signature") ErrGitWebhookTooLarge = errors.New("the body is over 5 MiB") )
Refusals of a delivery, answered 404, 401 and 413.
var BackupHoldPath = "/var/lib/casaos/backups/holding.json"
BackupHoldPath records which apps are being held still, and which containers were running when they were.
var BackupLogPath = "/var/lib/casaos/backups/runs.json"
BackupLogPath is where the record lives. A variable so a test can put it somewhere of its own.
var BackupSchedulePath = "/var/lib/casaos/backups/schedules.json"
BackupSchedulePath is where the standing arrangements live.
var ErrAppStoreSourceExists = fmt.Errorf("appstore source already exists")
var ErrGitAppNotFound = errors.New("not an app deployed from a git repository")
ErrGitAppNotFound is an app that is neither registered nor adoptable.
var ErrGitNoComposeFile = errors.New("the repository has no compose.yaml, compose.yml, docker-compose.yaml or docker-compose.yml at its root, and the app's .env names none in COMPOSE_FILE")
ErrGitNoComposeFile is a repository `docker compose up` would refuse at its root.
var ErrLogsSettings = errors.New("logs settings out of range")
ErrLogsSettings wraps a settings value out of range: the caller's mistake, not a failure.
var ErrUpdateAllRunning = errors.New("an update of every app is already running")
ErrUpdateAllRunning is answered to a second run asked for while one is going.
var (
NewVersionApp map[string]string // TODO - make use of NewVersionApp map
)
var NoUpdateBlacklist = []string{
"johnguan/stable-diffusion-webui:latest",
}
the patch is have no choice the digest compare is not work for these images I don't know why, but I have to do this I will remove the patch after I rewrite the digest compare
var ResticCheckPath = "/var/lib/casaos/backups/restic-checks.json"
ResticCheckPath is where the last check of each destination is written down.
var StatsDir = "/var/lib/casaos/stats/app_management"
StatsDir is where the sampler keeps its history and its alerts.
Functions ¶
func ApplyDataPath ¶ added in v0.4.82
ApplyDataPath points stats, git-app state, backups and image-update cache at root. Empty root is /var/lib/casaos. gitAppsDataRoot and os-upgrade/busy stay on the box paths. restic.KeyDir follows root (same path as the packaged default). restic.CacheDir stays /var/cache/casaos/restic unless root is not the default.
func ApplyGitAppsDataRoot ¶ added in v0.4.82
func ApplyGitAppsDataRoot(v string)
func ApplyLogsSettings ¶ added in v0.4.85
ApplyLogsSettings validates, saves to the config file, purges now, and starts or stops following the containers of no app without a restart.
func ApplyRetention ¶ added in v0.4.50
ApplyRetention deletes the runs of `app` at `destination` beyond the last `keep`. Zero or less keeps everything. Scheduled runs and runs somebody asked for share the pool: a retention counts backups, not reasons.
func Begin ¶ added in v0.4.57
Begin claims app for an operation of the given kind, or refuses with ErrAppBusy. end releases it; calling it more than once is harmless.
func BrowseBackup ¶ added in v0.4.83
func BrowseBackup(ctx context.Context, browser BackupBrowser, destination, app, stamp, rel string) ([]rclone.File, error)
BrowseBackup lists one level of a backup: the folders that lead to its copies at the top, and what a copy holds below that. Folders first, then by name.
func BuildCatalog ¶
func BuildCatalog(storeRoot string) (map[string]*ComposeApp, error)
func CheckGitApps ¶ added in v0.4.57
CheckGitApps checks every registered git app, one after another, and starts what the automatic rebuild may deploy. An app busy with another operation is checked next time.
Nothing is checked while an operating system upgrade is open, looked at before each app rather than once: a tick is as long as its checks, and the upgrade can open between two of them. A check only reads, but with the git binary the upgrade replaces, and what it would write down is an error about that.
func CheckImageUpdates ¶
func CheckImageUpdates(ctx context.Context) (*codegen.ImageUpdateCheckResult, error)
CheckImageUpdates asks every installed app's registry what its tags point at now and compares that with the copy on disk.
This is what sees an app that came from no app store. The store's upgradable list compares tags against a catalogue, so it is blind to an app nobody publishes a catalogue entry for, and equally blind to a tag that has been republished under the same name -- which is what `latest` does every time.
func CheckImageUpdatesForApp ¶
func CheckImageUpdatesForApp(ctx context.Context, composeApp *ComposeApp) error
CheckImageUpdatesForApp answers for one app and records it, leaving every other app's answer alone.
This is what makes the per-app button honest. It is called `Check then update`, and for an app that came from no store there is nothing else to check: without this it would answer from whatever the last sweep happened to leave in the cache, or from nothing at all if no sweep has run.
func CheckRestorePaths ¶ added in v0.4.83
CheckRestorePaths refuses, before anything is started, a path that leaves the backup.
func CurrentUpdateAllRun ¶ added in v0.4.52
func CurrentUpdateAllRun() *codegen.ComposeAppUpdateRun
CurrentUpdateAllRun is a copy of the run in progress or of the last one, or nil when none has run since this service started.
func DanglingImages ¶
func DanglingImages(ctx context.Context) (*codegen.DanglingImages, error)
DanglingImages reports what PruneDanglingImages would free, without freeing it.
func DeleteGitApp ¶ added in v0.4.57
DeleteGitApp removes a registered app no deployment has succeeded for: what a failed first deployment left (its containers, networks and images), the clone of a created app, its state and its secrets.
func FollowedContainers ¶ added in v0.4.85
FollowedContainers is the ids of the containers a follower reads right now.
func GenerateYAMLFromComposeApp ¶
func GenerateYAMLFromComposeApp(compose ComposeApp) ([]byte, error)
GenerateYAMLFromComposeApp writes an app loaded by the settings pipeline (ComposeAppFromSettingsYAML, LoadComposeAppForEditing) back to compose text; its strings are already escaped for compose.
func GitAppGrid ¶ added in v0.4.57
func GitAppGrid(app string) *codegen.GitAppGrid
GitAppGrid is what the app card shows of a registered git app, nil for any other app. It reads the state file alone: the grid never waits on git or on a registry.
func GitAppNames ¶ added in v0.4.57
GitAppNames is every registered git app, sorted.
func GitAppsWithoutContainers ¶ added in v0.4.57
func GitAppsWithoutContainers(listed []string) []codegen.WebAppGridItem
GitAppsWithoutContainers are the grid items of the registered git apps missing from the compose list, which only knows apps that have containers: never deployed, or left without one by a failed first deployment. The owner still reaches their Repository tab.
func ImageUpdateAvailable ¶
ImageUpdateAvailable reports whether an update is on offer for an app, or nil if nothing has looked since this service started. Nil is not "up to date": the dashboard shows a badge for true and nothing for either other case, and conflating them would claim an app is current when nobody has looked.
func IsDue ¶ added in v0.4.37
func IsDue(schedule BackupSchedule, now time.Time) (bool, error)
IsDue reports whether this schedule has a slot it has not run yet.
func LoadCategoryMap ¶
func LoadCategoryMap(storeRoot string) map[string]codegen.CategoryInfo
func LoadImageUpdates ¶
func LoadImageUpdates()
LoadImageUpdates restores what the last run found, so the dashboard's first paint after a restart shows the badges it showed before it.
func LoadRecommend ¶
func Logs ¶ added in v0.4.85
Logs is the open store, nil while it is not open (the logs routes answer 503).
func NewResticChecker ¶ added in v0.4.82
func NewResticChecker() resticChecker
NewResticChecker is what the tick drives in production.
func NewScheduledBackupRunner ¶ added in v0.4.37
func NewScheduledBackupRunner() *scheduledBackupRunner
NewScheduledBackupRunner is what the scheduler drives in production.
func ParseEnvFile ¶
ParseEnvFile validates `.env` text as the runtime will read it; a reserved key is refused rather than ignored.
func PlanUpdateAll ¶ added in v0.4.52
func PlanUpdateAll(apps map[string]*ComposeApp, available func(*ComposeApp) bool, storeCompose func(string) (*ComposeApp, error)) []codegen.ComposeAppUpdatePlanApp
PlanUpdateAll says what updating every app at once would do, from what is already known: the catalogue, and the last image check. It asks no registry; the caller does first, if it wants the answer fresh. `available` is the update button's own answer for an app, `storeCompose` the catalogue's compose file for a store app.
func PruneDanglingImages ¶
func PruneDanglingImages(ctx context.Context) (*codegen.DanglingImages, error)
PruneDanglingImages deletes them and reports what the daemon says it actually freed, which can be less than DanglingImages estimated if something claimed a layer since.
func PublishEventWrapper ¶
func ReceiveGitWebhook ¶ added in v0.4.61
ReceiveGitWebhook is a forge's delivery for app: the answer the forge is given, or one of the refusals. A refused delivery writes nothing, and no body, no header's value and no signature reaches the log.
func RecordAppEvent ¶ added in v0.4.85
func RecordAppEvent(app, kind, fromImage, toImage, detail string)
RecordAppEvent records what an operation did to an app; nothing while the store is not open.
func RecordBackupRun ¶ added in v0.4.37
func RecordBackupRun(record BackupRunRecord) error
RecordBackupRun appends one run and keeps the file to its last few hundred.
A record that cannot be written is an error the caller hears about rather than a silence: losing the run is bad, and not knowing the log is broken is worse.
func RecoverGitApps ¶ added in v0.4.57
RecoverGitApps runs once, at startup. An operation the process did not live to finish is recorded as interrupted and cleared, and nothing is retried: the commit joins those the automatic rebuild leaves alone. The worktrees of builds and the clones cut short are removed.
func RecoverHeldApps ¶ added in v0.4.38
func RecoverHeldApps(docker containerStopStarter)
RecoverHeldApps starts back anything a backup stopped and never restarted.
Run at startup. Starting a container that is already running is what StartContainer does nothing about, so this is safe to run when the note is merely stale -- and a stale note is a far better problem than an app nobody noticed was off.
func ResticChecks ¶ added in v0.4.82
func ResticChecks() (map[string]ResticCheck, error)
ResticChecks reads what is known of the last checks, by destination.
func RootFor ¶ added in v0.4.37
RootFor is where one run of a backup goes on a destination.
The app first, then the moment, so a destination holding several apps stays readable in a file browser and one app's history sits together. `stamp` is passed in rather than taken from the clock here: a plan that reads the clock cannot be tested, and the caller already knows when the run started.
func RunDueBackups ¶ added in v0.4.37
RunDueBackups runs everything that has an unrun slot, records each one, and applies retention.
It returns rather than loops, and takes `now`, so the whole of the decision can be exercised in a test. main drives it from the crontab that is already there,
which is the only part that reads a clock.
A schedule's LastRun is written whether the run SUCCEEDED or not. Retrying a failing backup every minute until the disk fills is worse than waiting for tomorrow's slot, and the failure is in the log where somebody can see it. It is written when that backup ends, in the file as it is then, not for all of them when the tick does: a tick can last hours, the schedules can be saved meanwhile, and a process that stops half way owes only what it did not do.
One tick at a time. Cron starts one every minute whether the last is over or not, and a tick that finds another running leaves its backups to it: what falls due meanwhile is found by the tick after that one ends. Otherwise the backups owed after a hold, which can take longer than a minute, would be started again by every tick until they were recorded.
While an operating system upgrade is open nothing runs and nothing is recorded: the slot stays unrun, so what was skipped runs once when the upgrade is closed. The tick says so in one line.
func RunDueResticChecks ¶ added in v0.4.82
RunDueResticChecks checks the destinations kept with restic that are due: never checked, last checked a week ago, or a day ago when that check found a fault.
Like the backups it waits while an operating system upgrade is open, and it checks one destination at a time. A tick that finds another running leaves the work to it.
func RunsToPurge ¶ added in v0.4.37
RunsToPurge names the runs that fall outside a retention of `keep`.
Runs are named by their stamp, which sorts, so the newest are simply the last. `keep` of zero or less keeps everything -- deleting backups is not something to do because a field defaulted, and a schedule saved without a retention means "no policy", not "keep none".
One run is always kept whatever the arithmetic says. A retention that empties a destination is not a retention.
func SaveBackupSchedules ¶ added in v0.4.37
func SaveBackupSchedules(schedules []BackupSchedule) error
SaveBackupSchedules replaces the lot.
func SetLogs ¶ added in v0.4.85
SetLogs puts s in place of the running store, for tests; nil means closed.
func SetStats ¶ added in v0.4.70
func SetStats(s *dockerstats.Sampler)
SetStats puts a sampler in place of the running one, for tests.
func SplitInstallEnv ¶ added in v0.4.74
SplitInstallEnv takes the .env out of compose text (x-casaos-env) and returns the text without it. Text without the key comes back unchanged, byte for byte.
func Standardize ¶
func StartLogs ¶ added in v0.4.85
StartLogs returns at once: a goroutine opens the store, retrying while its root cannot be created, follows the running containers, and purges at open, daily and when over the cap.
func StartResticCheck ¶ added in v0.4.82
func StartResticCheck(ctx context.Context, now time.Time, checker resticChecker, destination string) (<-chan struct{}, bool)
StartResticCheck checks one destination now, in the background, and says whether it started: not while another check runs, since each one reads the whole repository. The channel closes when the check is over.
func StartStats ¶ added in v0.4.70
StartStats opens the sampler and runs it until ctx ends or StopStats; it does nothing when the sampler is already running. An error leaves it not running: the stats routes answer 503 and the rest of the service is unaffected.
func StartUpdateAll ¶ added in v0.4.52
func StartUpdateAll(ids []string, update func(ctx context.Context, id string) (codegen.ComposeAppUpdateRunAppState, string)) (*codegen.ComposeAppUpdateRun, error)
StartUpdateAll runs the apps named, in that order, one after another, and returns the run as it starts. `update` does one app and says what became of it.
func Stats ¶ added in v0.4.70
func Stats() *dockerstats.Sampler
Stats is the running sampler, nil when it is not running.
func StopLogs ¶ added in v0.4.85
func StopLogs()
StopLogs stops the followers and closes the store; it does nothing when the collector is not running.
func StopStats ¶ added in v0.4.70
func StopStats()
StopStats stops the sampler and closes its files; it does nothing when the sampler is not running.
func UpdateOneForRun ¶ added in v0.4.52
UpdateOneForRun is what the run does to one app: what the update button does, the check its name promises included, waited for; and the reason, when nothing happens.
func VolumesToRemove ¶ added in v0.4.38
func VolumesToRemove(volumes []ContainerVolume, requested []string) (remove []string, refused map[string]string)
VolumesToRemove filters what the caller asked to delete down to what may be.
The caller's list is never trusted: a screen drawn a minute ago is a screen that has been overtaken, and by the time somebody presses the button another container may have started using one of these. Everything is re-decided here against what the daemon says now.
A name the container does not mount at all is refused outright rather than passed through -- that is the shape of a request that would delete somebody else's volume through this container's door.
func WatchDocker ¶ added in v0.4.67
WatchDocker follows Docker's events for the apps' containers until ctx ends, and publishes what they mean. A stream that breaks -- Docker restarting, or not up yet -- is followed again after a pause that doubles up to longestPause.
Types ¶
type App ¶
type App types.ServiceConfig
type AppOperation ¶ added in v0.4.65
AppOperation is one app something runs on, and the kind of operation it is.
func RunningOperations ¶ added in v0.4.65
func RunningOperations() []AppOperation
RunningOperations lists what runs on the apps right now: what Begin holds, and what markInProgress says runs without a hold. One entry per app, the hold's kind over a mark's, sorted by app so that the same operations always read the same. Empty when nothing runs: the core asks before it updates the box by itself, and waits while anything is listed.
type AppStore ¶
type AppStore interface {
Catalog() (map[string]*ComposeApp, error)
CategoryMap() (map[string]codegen.CategoryInfo, error)
ComposeApp(id string) (*ComposeApp, error)
Recommend() ([]string, error)
UpdateCatalog() error
WorkDir() (string, error)
}
func AppStoreByURL ¶
func NewDefaultAppStore ¶
type AppStoreManagement ¶
type AppStoreManagement struct {
// contains filtered or unexported fields
}
func NewAppStoreManagement ¶
func NewAppStoreManagement() *AppStoreManagement
func (*AppStoreManagement) AppStoreList ¶
func (a *AppStoreManagement) AppStoreList() []codegen.AppStoreMetadata
func (*AppStoreManagement) AppStoreMap ¶
func (a *AppStoreManagement) AppStoreMap() (map[string]AppStore, error)
func (*AppStoreManagement) Catalog ¶
func (a *AppStoreManagement) Catalog() (map[string]*ComposeApp, error)
func (*AppStoreManagement) CategoryMap ¶
func (a *AppStoreManagement) CategoryMap() (map[string]codegen.CategoryInfo, error)
AppStore interface
func (*AppStoreManagement) ChangeGlobal ¶
func (a *AppStoreManagement) ChangeGlobal(key string, value string) error
func (*AppStoreManagement) ComposeApp ¶
func (a *AppStoreManagement) ComposeApp(id string) (*ComposeApp, error)
func (*AppStoreManagement) DeleteGlobal ¶
func (a *AppStoreManagement) DeleteGlobal(key string) error
func (*AppStoreManagement) FinishUpgrade ¶
func (a *AppStoreManagement) FinishUpgrade(appID string)
func (*AppStoreManagement) ForgetUpgradable ¶
func (a *AppStoreManagement) ForgetUpgradable(appID string)
ForgetUpgradable drops one app's cached answer so the next question is asked afresh. An hour of cache is right for a list rendered in the background and wrong immediately after someone pressed a button with the word "check" on it.
func (*AppStoreManagement) IsUpdateAvailable ¶
func (a *AppStoreManagement) IsUpdateAvailable(composeApp *ComposeApp) bool
func (*AppStoreManagement) IsUpdateAvailableWith ¶
func (a *AppStoreManagement) IsUpdateAvailableWith(composeApp *ComposeApp, storeComposeApp *ComposeApp) (bool, string, error)
IsUpdateAvailableWith decides an update SERVICE BY SERVICE, because that is what an update writes: every service the catalogue names takes the catalogue's image.
It is on offer when no service would go backwards and something would actually change -- the catalogue names an image this app does not run, or the registries say an image it does run has moved. Reading the MAIN service's tag alone made a catalogue that bumps only a sidecar invisible, which is what froze the multi-service stacks; and demanding every image be identical before believing the registries threw their answer away over any difference at all, including a service the owner added by hand.
It answers with a reason whenever it refuses, because "no update, and nothing on screen saying why" is indistinguishable from a broken button.
func (*AppStoreManagement) IsUpdating ¶
func (a *AppStoreManagement) IsUpdating(appID string) bool
func (*AppStoreManagement) OnAppStoreRegister ¶
func (a *AppStoreManagement) OnAppStoreRegister(fn func(string) error)
func (*AppStoreManagement) OnAppStoreUnregister ¶
func (a *AppStoreManagement) OnAppStoreUnregister(fn func(string) error)
func (*AppStoreManagement) Recommend ¶
func (a *AppStoreManagement) Recommend() ([]string, error)
func (*AppStoreManagement) RegisterAppStore ¶
func (a *AppStoreManagement) RegisterAppStore(ctx context.Context, appstoreURL string, callbacks ...func(*codegen.AppStoreMetadata)) error
func (*AppStoreManagement) RegisterAppStoreSync ¶
func (a *AppStoreManagement) RegisterAppStoreSync(ctx context.Context, appstoreURL string, callbacks ...func(*codegen.AppStoreMetadata)) error
TODO: refactor the function and above function
func (*AppStoreManagement) StartUpgrade ¶
func (a *AppStoreManagement) StartUpgrade(appID string)
func (*AppStoreManagement) UnregisterAppStore ¶
func (a *AppStoreManagement) UnregisterAppStore(appStoreID uint) error
func (*AppStoreManagement) UpdateAvailability ¶
func (a *AppStoreManagement) UpdateAvailability(composeApp *ComposeApp) (bool, string)
UpdateAvailability is IsUpdateAvailable plus why not. The reason is empty when an update is on offer and when the app is simply current; it is filled only when the catalogue holds something this app must not be given -- an older image for one of its services, a service an update cannot create, a pair of tags nothing can order.
func (*AppStoreManagement) UpdateCatalog ¶
func (a *AppStoreManagement) UpdateCatalog() error
func (*AppStoreManagement) WorkDir ¶
func (a *AppStoreManagement) WorkDir() (string, error)
type BackupBrowser ¶ added in v0.4.83
type BackupBrowser interface {
BackupRestorer
List(ctx context.Context, destinationName, remotePath, sub string) ([]rclone.File, error)
}
BackupBrowser is the half of the engine a look inside a backup needs.
type BackupCopier ¶ added in v0.4.37
type BackupCopier interface {
CopyDirectory(ctx context.Context, source, destinationName, destinationPath string) error
CopyFile(ctx context.Context, source, destinationName, destinationPath string) error
}
BackupCopier is the destination, as this needs it. *rclone.Client is one.
type BackupEntry ¶ added in v0.4.37
type BackupEntry struct {
Kind BackupKind `json:"kind"`
// Path on the host. Empty for an entry that was skipped before it could be
// resolved, and for a named volume until Docker has been asked where it lives.
Path string `json:"path"`
// Service that mounts it, empty for the app's own files
Service string `json:"service,omitempty"`
// Target inside the container, for a volume or a bind
Target string `json:"target,omitempty"`
// Volume name as the compose file writes it, for a named volume
Volume string `json:"volume,omitempty"`
// Skip is why this is not going in the backup. Empty means it is.
Skip string `json:"skip,omitempty"`
}
BackupEntry is one thing on disk that belongs to an app -- or one that looked like it did and does not, which is worth saying rather than dropping.
func SystemBackupInventory ¶ added in v0.4.45
func SystemBackupInventory(exists func(string) bool) []BackupEntry
SystemBackupInventory lists what the box is, as backup entries.
func (BackupEntry) IsIncluded ¶ added in v0.4.37
func (e BackupEntry) IsIncluded() bool
IsIncluded reports whether this entry is part of the backup.
type BackupGit ¶ added in v0.4.57
type BackupGit struct {
Remote string `json:"remote"`
Branch string `json:"branch"`
Commit string `json:"commit"`
// An app that follows tags: the mode, the tag the commit was deployed as, and the tag
// filter. Omitted for a branch app, whose origin stays what older versions wrote.
Follow string `json:"follow,omitempty"`
Tag string `json:"tag,omitempty"`
TagPattern string `json:"tag_pattern,omitempty"`
Prereleases bool `json:"prereleases,omitempty"`
}
BackupGit is a git app's origin as a backup records it. Never a key or a token.
type BackupHeldApp ¶ added in v0.4.43
type BackupHeldApp struct {
App string `json:"app"`
// Installed says whether this box runs the app now. A restore of one that is
// not installs it first, from the backup's own compose file, which is worth
// saying before the button is pressed.
Installed bool `json:"installed"`
// Stamps of its runs, newest first.
Stamps []string `json:"stamps"`
}
BackupHeldApp is one app a destination holds backups of.
func DestinationContents ¶ added in v0.4.43
func DestinationContents(_ context.Context, lister backupLister, destination string, installed map[string]bool) ([]BackupHeldApp, error)
DestinationContents lists the apps a destination holds backups of, and their runs, newest first. installed is what this box runs now.
type BackupKind ¶ added in v0.4.37
type BackupKind string
BackupKind says why a path is in the list, which is also what a restore needs to know to put it back.
const ( // BackupKindCompose is the app's docker-compose.yml BackupKindCompose BackupKind = "compose" // BackupKindEnv is the .env beside it BackupKindEnv BackupKind = "env" // BackupKindVolume is a named volume, which Docker keeps somewhere of its own BackupKindVolume BackupKind = "volume" // BackupKindBind is a host path a service mounts directly BackupKindBind BackupKind = "bind" )
type BackupManifest ¶ added in v0.4.37
type BackupManifest struct {
// FormatVersion so a later reader knows what it is looking at. A backup outlives
// the code that wrote it, which is the whole point of one.
FormatVersion int `json:"format_version"`
App string `json:"app"`
// Operations, in the order they were planned
Operations []BackupOperation `json:"operations"`
// Bytes is the size of what the operations copied, measured on this host as
// each one went: a file's size, the files under a folder added up. What the
// destination holds may be less (restic keeps one copy of a block it has seen).
Bytes int64 `json:"bytes,omitempty"`
// Skipped says what was deliberately left out, with the reason, so the person
// restoring finds out here rather than by noticing something missing later.
Skipped []BackupEntry `json:"skipped,omitempty"`
// ContainersStopped records whether the app was held still while this was
// copied. A backup taken from a running database may not restore, and the only
// thing worse than knowing that is not knowing it.
ContainersStopped bool `json:"containers_stopped"`
// Git is where the code of an app deployed from its repository comes from. Optional, so
// the format version stays: an older reader ignores it.
Git *BackupGit `json:"git,omitempty"`
}
BackupManifest is written at the root of every backup, and is the only thing a restore needs to read.
func BackupOnDemand ¶ added in v0.4.39
func BackupOnDemand(ctx context.Context, app *ComposeApp, docker backupDocker, copier BackupCopier, opts BackupOptions) (BackupManifest, error)
BackupOnDemand runs one backup somebody asked for, and records it.
func PlanBackup ¶ added in v0.4.37
func PlanBackup(app string, inventory []BackupEntry, volumeMountpoints map[string]string) BackupManifest
PlanBackup turns an app's inventory into the copies that would make it.
`volumeMountpoints` maps a compose volume name to where Docker actually keeps it. A volume with no entry is one the daemon could not place, and it is moved to the skipped list rather than copied from a guessed path -- a guess that happens to name an empty folder produces a backup that reports success and restores nothing.
func PlanSystemBackup ¶ added in v0.4.45
func PlanSystemBackup(exists func(string) bool) BackupManifest
PlanSystemBackup is the plan for the box, worked out from what is here now.
func RunBackup ¶ added in v0.4.37
func RunBackup(ctx context.Context, app *ComposeApp, docker backupDocker, copier BackupCopier, opts BackupOptions) (BackupManifest, error)
RunBackup copies an app to a destination and returns the manifest it wrote.
The manifest goes last, after everything it describes. A run interrupted half-way leaves files and no manifest, which reads as what it is -- an incomplete backup -- rather than as a complete one missing its contents.
func RunSystemBackup ¶ added in v0.4.45
func RunSystemBackup(ctx context.Context, copier BackupCopier, units unitControl, opts SystemBackupOptions) (BackupManifest, error)
RunSystemBackup copies the box's own state to a destination.
func SystemBackupOnDemand ¶ added in v0.4.45
func SystemBackupOnDemand(ctx context.Context, copier BackupCopier, units unitControl, opts SystemBackupOptions) (BackupManifest, error)
SystemBackupOnDemand runs one backup of the box and records it.
func (*BackupManifest) MarkBindFiles ¶ added in v0.4.37
func (m *BackupManifest) MarkBindFiles(isFile func(path string) bool)
MarkBindFiles corrects the operations whose source turned out to be a single file rather than a folder.
A bind mounts either, and the compose file does not say which -- `./php.ini` and `./config` look identical until something checks the disk. rclone needs to be told: a file copied as though it were a directory copies its whole parent, which on `/DATA/photos/one.jpg` means the entire photo library.
`isFile` is passed in rather than called here so the planner stays testable without a filesystem.
func (*BackupManifest) TotalBySource ¶ added in v0.4.37
func (m *BackupManifest) TotalBySource() []string
TotalBySource is what a plan addresses, deduplicated and sorted: two services mounting the same folder is one thing to copy, not two.
type BackupOperation ¶ added in v0.4.37
type BackupOperation struct {
Kind BackupKind `json:"kind"`
// Source on this host
Source string `json:"source"`
// Destination relative to the root of this backup, always with forward slashes
Destination string `json:"destination"`
// Directory is true for a folder, false for a single file. rclone copies the
// two with different calls, and getting it wrong copies a file's parent.
Directory bool `json:"directory"`
// Volume is the compose name of the volume this came from, when it came from one
Volume string `json:"volume,omitempty"`
// Service that mounted it, and where
Service string `json:"service,omitempty"`
Target string `json:"target,omitempty"`
}
BackupOperation is one copy: one host path to one place under the backup root.
type BackupOptions ¶ added in v0.4.37
type BackupOptions struct {
// Destination is the name of an rclone remote this project manages
Destination string
// Stamp names this run inside the destination. Passed in, not read from a
// clock, so a caller can name a run and find it again.
Stamp string
// HoldStill stops the app's containers for the length of the copy. Off means a
// copy taken from a running app, which for anything holding a database may not
// restore -- the manifest records which was done.
HoldStill bool
// Containers answers which containers the app has, for HoldStill. Nil means ask
// the app, which is what production does; a test supplies its own so that
// exercising the hold does not require a daemon -- and does not leave the
// daemon's client goroutines behind for the next test to trip over.
Containers func(context.Context) (map[string][]codegen.ContainerSummary, error)
}
BackupOptions is one run.
type BackupPartialRestorer ¶ added in v0.4.83
type BackupPartialRestorer interface {
BackupRestorer
CopyBack(ctx context.Context, destinationName, remotePath, sub, hostPath string) error
}
BackupPartialRestorer is what taking some paths of a backup back needs.
type BackupRestorer ¶ added in v0.4.41
type BackupRestorer interface {
RestoreDirectory(ctx context.Context, destinationName, remotePath, hostPath string) error
RestoreFile(ctx context.Context, destinationName, remotePath, hostPath string) error
Fetch(ctx context.Context, destinationName, remotePath string) ([]byte, error)
}
BackupRestorer is the half of the rclone client a restore needs.
type BackupRunRecord ¶ added in v0.4.37
type BackupRunRecord struct {
App string `json:"app"`
Destination string `json:"destination"`
Stamp string `json:"stamp"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at"`
// Scheduled tells a run that went off by itself from one somebody asked for.
Scheduled bool `json:"scheduled"`
// ContainersStopped says whether the app was held still, which decides whether
// anything holding a database can be trusted to restore.
ContainersStopped bool `json:"containers_stopped"`
Copied int `json:"copied"`
// Bytes is the size of what was read for the copy, from the manifest; 0 for a
// restore, and for a run too old to have measured it.
Bytes int64 `json:"bytes"`
SkippedCount int `json:"skipped_count"`
// Error is empty when the run succeeded.
Error string `json:"error,omitempty"`
// Restore marks a run that put the app back from a backup rather than one
// that took a backup. Same log, because a person looking for what happened
// to an app wants both in one place, newest first.
Restore bool `json:"restore,omitempty"`
}
BackupRunRecord is one run, finished or failed.
func BackupRuns ¶ added in v0.4.37
func BackupRuns() ([]BackupRunRecord, error)
BackupRuns is what happened, newest first.
func (BackupRunRecord) Succeeded ¶ added in v0.4.37
func (r BackupRunRecord) Succeeded() bool
Succeeded reports whether this run finished.
type BackupSchedule ¶ added in v0.4.37
type BackupSchedule struct {
App string `json:"app"`
Destination string `json:"destination"`
Every ScheduleEvery `json:"every"`
// At is local time, "HH:MM". Local because somebody chose it looking at their
// own clock, and "back up at 3am" means their 3am.
At string `json:"at"`
// Weekday applies to weekly only.
Weekday time.Weekday `json:"weekday"`
// Keep is how many runs survive at the destination. Zero keeps everything,
// which is the safe reading of a field nobody filled in -- deleting backups is
// not something to do because a number defaulted.
Keep int `json:"keep"`
// HoldStill stops the app for the copy.
HoldStill bool `json:"hold_still"`
Enabled bool `json:"enabled"`
// LastRun is when this last went off, and the only thing that stops it going
// off again for the same slot.
LastRun time.Time `json:"last_run"`
}
BackupSchedule is one standing arrangement: this app, to this destination, at this time.
func BackupSchedules ¶ added in v0.4.37
func BackupSchedules() ([]BackupSchedule, error)
BackupSchedules reads the standing arrangements.
A file that will not parse is an error, not an empty list: starting over silently would switch off every backup on the box and leave nothing saying so.
func DueSchedules ¶ added in v0.4.37
func DueSchedules(schedules []BackupSchedule, now time.Time) (due []BackupSchedule, broken []error)
DueSchedules is every schedule with an unrun slot, in a stable order.
A schedule whose time of day cannot be read is reported rather than skipped: silently ignoring it is how a box backs nothing up for months while its interface says it is scheduled.
type ComposeApp ¶
type ComposeApp codegen.ComposeApp
func ComposeAppFromSettingsYAML ¶
func ComposeAppFromSettingsYAML(buf []byte, keep map[string]struct{}) (*ComposeApp, error)
ComposeAppFromSettingsYAML parses the compose YAML the settings page (or any YAML-speaking editor) sends back to ApplyComposeAppSettings. Interpolation must stay ON: that YAML came from GenerateYAMLFromComposeApp and already has `$` escaped as `$$`; substituteEscaping collapses it and escapes the result exactly once (#1988). keep is ComposeApp.EnvKeys(): references to the app's `.env` are copied verbatim instead of being resolved.
func LoadComposeAppForEditing ¶
func LoadComposeAppForEditing(appID, configFile string, keep map[string]struct{}) (*ComposeApp, error)
LoadComposeAppForEditing loads an installed app for the settings editor: a reference to a key of keep or to a key the runtime defines (settingsKeep) comes out as written, a bare `KEY` entry for one of them as `${KEY}`, and `.env` itself is never read. LoadComposeAppFromConfigFile (what docker runs) keeps resolving everything.
func LoadComposeAppFromConfigFile ¶
func LoadComposeAppFromConfigFile(appID string, configFile string) (*ComposeApp, error)
func NewComposeAppFromYAML ¶
func NewComposeAppFromYAML(yaml []byte, skipInterpolation, skipValidation bool) (*ComposeApp, error)
func NewComposeAppWithEnv ¶ added in v0.4.74
func NewComposeAppWithEnv(text, env []byte) (resolved, kept *ComposeApp, err error)
NewComposeAppWithEnv loads compose text installed with its .env, twice. resolved takes the .env's values, an `env_file: .env` included: it is what the checks before the install look at (the ports). kept leaves every reference to a key of the .env as written: it is what the install writes beside the .env, so that the app's .env settings go on applying, as for an app whose .env was added later (see EnvKeys). A key CasaOS sets itself is refused, as there. COMPOSE_PROFILES in the .env turns profiles on, as docker compose reads it: resolved, what the checks look at, has the services of the profiles it names; kept, what is written, has every service with its profiles, and the runtime reads the same .env to run the ones that are on.
func (*ComposeApp) App ¶
func (a *ComposeApp) App(name string) *App
TODO rename the function to service and add error return value
func (*ComposeApp) Apply ¶
func (a *ComposeApp) Apply(ctx context.Context, newComposeYAML []byte) error
func (*ComposeApp) ApplyEnv ¶
func (a *ComposeApp) ApplyEnv(ctx context.Context, env []byte) error
ApplyEnv replaces the `.env` of the app (empty removes it) and re-creates it from its unchanged compose file: a restart would keep the old environment, the apply reloads the project first.
func (*ComposeApp) Apps ¶
func (a *ComposeApp) Apps() map[string]*App
func (*ComposeApp) AuthorType ¶
func (a *ComposeApp) AuthorType() codegen.StoreAppAuthorType
func (*ComposeApp) BackupInventory ¶ added in v0.4.37
func (a *ComposeApp) BackupInventory() []BackupEntry
BackupInventory is everything on disk that belongs to this app, decided without copying anything and without asking Docker.
It lists what is left OUT as well as what goes in, each with its reason. A backup that quietly drops a mount is discovered on the day it is restored, which is the worst possible day to discover it.
A named volume comes back carrying its name and no path: where Docker put it is the daemon's answer, asked separately, and the list of which volumes belong to this app does not depend on the daemon being reachable.
func (*ComposeApp) Containers ¶
func (a *ComposeApp) Containers(ctx context.Context) (map[string][]api.ContainerSummary, error)
func (*ComposeApp) Create ¶
func (a *ComposeApp) Create(ctx context.Context, options api.CreateOptions, service api.Compose) error
func (*ComposeApp) DockerVolumeName ¶ added in v0.4.37
func (a *ComposeApp) DockerVolumeName(composeName string) string
DockerVolumeName is the name Docker actually knows a compose volume by.
A compose file says `backend-storage`; Docker stores it as `<project>_backend-storage` unless the file declares a name of its own or marks it external. compose-go has already worked that out at load time, so the answer is read rather than rebuilt -- rebuilding it is how a backup addresses a volume that does not exist and reports success having copied nothing.
func (*ComposeApp) EnvFile ¶
func (a *ComposeApp) EnvFile() string
EnvFile is the `.env` docker compose reads next to the compose file of an installed app.
func (*ComposeApp) EnvFileText ¶
func (a *ComposeApp) EnvFileText() ([]byte, error)
EnvFileText returns the raw `.env`, empty when there is none.
func (*ComposeApp) EnvKeys ¶
func (a *ComposeApp) EnvKeys() (map[string]struct{}, error)
EnvKeys is the keep-set of the settings round trip: the names defined in the app's `.env`. Values of those stay `${KEY}` references in docker-compose.yml instead of being baked in, otherwise the first settings save would copy the secrets into the compose file and every later `.env` edit would be a no-op. A reserved key (see reservedEnvKey) is left out: the editor then shows the value the runtime uses. A missing file yields an empty set; an unreadable one is an error, an empty set would bake every value on the next save.
func (*ComposeApp) GetPortsInUse ¶
func (a *ComposeApp) GetPortsInUse() (*codegen.ComposeAppValidationErrorsPortsInUse, error)
func (*ComposeApp) GetPortsInUseFor ¶ added in v0.4.77
func (a *ComposeApp) GetPortsInUseFor(profiles []string) (*codegen.ComposeAppValidationErrorsPortsInUse, error)
GetPortsInUseFor is GetPortsInUse for what runs with the profiles on: the ports of a service behind a profile that is off (a proxy that is not installed here) are no conflict.
func (*ComposeApp) HealthCheck ¶
func (a *ComposeApp) HealthCheck() (bool, error)
func (*ComposeApp) IsUncontrolled ¶
func (a *ComposeApp) IsUncontrolled() bool
IsUncontrolled reports whether the app declares `x-casaos.is_uncontrolled`.
The read this replaces was one expression whose FIRST type assertion had no comma-ok:
Extensions[x].(map[string]interface{})[y].(bool)
The guard above it tests whether the KEY is present, which is not the same as the value being a map: `x-casaos:` with nothing after it parses to a nil value under a present key, and asserting that to a map type panics -- taking down whichever service called it. The copy of this line in the app grid did exactly that to a whole host in v0.4.61.
Indexing a nil map is fine in Go. Asserting a nil interface to a map type is not.
func (*ComposeApp) Logs ¶
Logs returns the logs of the app. Naming services narrows it to those; naming none keeps every service of the app, which is what the app-wide viewer asks for.
func (*ComposeApp) MainService ¶
func (a *ComposeApp) MainService() (*App, error)
MainService is the service MainServiceName names. Reading it out of StoreInfo instead meant a stack with no `x-casaos` had no main service at all, and every caller of this and of MainTag failed on it -- including the check that runs when the owner saves the settings of such an app.
func (*ComposeApp) MainServiceName ¶
func (a *ComposeApp) MainServiceName() string
MainServiceName is the service an app's interface is about: what `x-casaos.main` names, and otherwise the alphabetically first service, which is the rule StoreInfo has always applied when that key is absent.
A stack somebody assembled by hand has no `x-casaos` at all, and gets the same fallback rather than no answer. The extension carries presentation; which service leads is a question about the compose file, and it has an answer either way.
func (*ComposeApp) MainTag ¶
func (a *ComposeApp) MainTag() (string, error)
func (*ComposeApp) PullAndApply ¶
func (a *ComposeApp) PullAndApply(ctx context.Context, newComposeYAML []byte) error
func (*ComposeApp) PullAndInstall ¶
func (a *ComposeApp) PullAndInstall(ctx context.Context) error
func (*ComposeApp) SetStatus ¶
func (a *ComposeApp) SetStatus(ctx context.Context, status codegen.RequestComposeAppStatus) error
func (*ComposeApp) SetStoreAppID ¶
func (a *ComposeApp) SetStoreAppID(storeAppID string) (string, bool)
func (*ComposeApp) SetTitle ¶
func (a *ComposeApp) SetTitle(title, lang string)
func (*ComposeApp) SetUncontrolled ¶
func (a *ComposeApp) SetUncontrolled(uncontrolled bool) error
func (*ComposeApp) StoreInfo ¶
func (a *ComposeApp) StoreInfo(includeApps bool) (*codegen.ComposeAppStoreInfo, error)
func (*ComposeApp) SupportsArchitecture ¶
func (a *ComposeApp) SupportsArchitecture(arch string) bool
SupportsArchitecture reports whether the compose app runs on arch (a runtime.GOARCH value: amd64, arm64, arm). A missing or empty `architectures` list means every architecture, matching the UI (AppPanel.vue `unuseable`).
func (*ComposeApp) Uninstall ¶
func (a *ComposeApp) Uninstall(ctx context.Context, deleteConfigFolder bool) error
func (*ComposeApp) UpWithCheckRequire ¶
func (*ComposeApp) Update ¶
func (a *ComposeApp) Update(ctx context.Context) error
Update is what the update button does: the app is replaced in the background and the request returns at once; the events of the update say the rest.
func (*ComposeApp) UpdateEventPropertiesFromStoreInfo ¶
func (a *ComposeApp) UpdateEventPropertiesFromStoreInfo(eventProperties map[string]string) error
Try to update AppIcon and AppTitle in given event properties from store info
func (*ComposeApp) UpdateNow ¶ added in v0.4.52
func (a *ComposeApp) UpdateNow(ctx context.Context) error
UpdateNow is Update waited for: the run of every app at once does them one after another (compose_update_all.go).
func (*ComposeApp) WriteEnvFile ¶
func (a *ComposeApp) WriteEnvFile(text []byte) error
WriteEnvFile replaces the `.env`; an empty body removes it.
type ComposeService ¶
type ComposeService struct {
// contains filtered or unexported fields
}
func NewComposeService ¶
func NewComposeService() *ComposeService
func (*ComposeService) Install ¶
func (s *ComposeService) Install(ctx context.Context, composeApp *ComposeApp, env []byte) error
Install writes the app's compose file, and env as its .env beside it when there is one (see NewComposeAppWithEnv), then installs it.
func (*ComposeService) IsInstalling ¶
func (s *ComposeService) IsInstalling(appName string) bool
func (*ComposeService) List ¶
func (s *ComposeService) List(ctx context.Context) (map[string]*ComposeApp, error)
func (*ComposeService) PrepareWorkingDirectory ¶
func (s *ComposeService) PrepareWorkingDirectory(name string) (string, error)
func (*ComposeService) RecoverAppsWaitingForStorage ¶
func (s *ComposeService) RecoverAppsWaitingForStorage(ctx context.Context)
RecoverAppsWaitingForStorage starts compose apps that were abandoned because the default storage was not ready when they first tried to start. It is meant to be called periodically after service startup; it stops acting once the recovery pass has run or the wait window has expired.
func (*ComposeService) Uninstall ¶
func (s *ComposeService) Uninstall(ctx context.Context, composeApp *ComposeApp, deleteConfigFolder bool) error
type ContainerVolume ¶ added in v0.4.38
type ContainerVolume struct {
// Name is the volume's name in Docker.
Name string `json:"name"`
// Target is where the container mounts it.
Target string `json:"target"`
// Size in bytes, or -1 when the driver cannot say. Shown so nobody deletes
// 1.4 GB thinking it is a cache.
Size int64 `json:"size"`
// Containers is how many containers reference it, this one included, or -1
// when the daemon cannot say.
Containers int64 `json:"containers"`
// Removable is false when this must not be offered for deletion at all.
Removable bool `json:"removable"`
// Reason says why, when it is not.
Reason string `json:"reason,omitempty"`
}
ContainerVolume is one named volume a container mounts.
func DescribeVolumesForRemoval ¶ added in v0.4.38
func DescribeVolumesForRemoval(volumes []ContainerVolume) []ContainerVolume
DescribeVolumesForRemoval marks which of a container's volumes may be offered.
A volume another container still uses is never offered: deleting it takes data out from under something that is running, and the person clicking is looking at a card for a different container entirely.
A daemon that cannot count references is treated as "cannot say", which is treated as "do not offer". Guessing wrong in that direction costs disk space; guessing wrong in the other costs somebody's database.
type DockerService ¶
type DockerService interface {
// image
IsExistImage(imageName string) bool
PullImage(ctx context.Context, imageName string) error
PullLatestImage(ctx context.Context, imageName string) (bool, error)
RemoveImage(name string) error
// container
CheckContainerHealth(id string) (bool, error)
CreateContainer(m model.CustomizationPostData, id string) (containerID string, err error)
CreateContainerShellSession(container, row, col string) (hr types.HijackedResponse, err error)
DescribeContainer(ctx context.Context, name string) (*types.ContainerJSON, error)
GetContainer(id string) (types.Container, error)
GetContainerAppList(name, image, state *string) (*[]model.MyAppList, *[]model.MyAppList)
GetContainerByName(name string) (*types.Container, error)
GetContainerLog(name string) ([]byte, error)
GetContainerStats() []model.DockerStatsModel
RecreateContainer(ctx context.Context, id string, pull bool, force bool) error
RemoveContainer(name string, update bool) error
RestartContainer(id string) error
SampleContainerStats(ctx context.Context, ids []string) map[string]*container.StatsResponse
VolumeMountpoints(ctx context.Context, names []string) map[string]string
VolumeUsage(ctx context.Context) map[string]docker.VolumeSpace
RemoveVolume(ctx context.Context, name string) error
RenameContainer(name, id string) (err error)
StartContainer(name string) error
StopContainer(id string) error
// network
GetNetworkList() []network.Summary
// docker server
GetServerInfo() (system.Info, error)
}
func NewDockerService ¶
func NewDockerService() DockerService
type ErrAppBusy ¶ added in v0.4.57
ErrAppBusy is the refusal, naming what holds the app. The API answers it with 409.
func (ErrAppBusy) Error ¶ added in v0.4.57
func (e ErrAppBusy) Error() string
type GitAppChanges ¶ added in v0.4.57
type GitAppChanges struct {
Branch *string
AutoDeploy *bool
Access *string
Token *string
// WebhookEnabled turns the webhook on, with a new secret when it has none, or off,
// forgetting the secret.
WebhookEnabled *bool
// RegenerateWebhookSecret replaces the secret at once; refused while the webhook is off.
RegenerateWebhookSecret *bool
// Follow changes the mode: the folder of a cloned app is put on the branch or detached.
// TagPattern and Prereleases change the tags an app that follows them may deploy.
Follow *string
TagPattern *string
Prereleases *bool
}
GitAppChanges changes a git app; nil leaves a field as it is.
type GitAppRegistration ¶ added in v0.4.57
type GitAppRegistration struct {
Name string
URL string
Branch string
Access string
Token string
// Follow is gitFollowBranch, empty for the same, or gitFollowTags; TagPattern and
// Prereleases choose the tags an app that follows them may deploy.
Follow string
TagPattern string
Prereleases bool
}
GitAppRegistration is an app to create from a repository.
type GitAppView ¶ added in v0.4.57
type GitAppView struct {
App string `json:"app"`
Origin string `json:"origin"`
Dir string `json:"dir"`
Remote string `json:"remote"`
Branch string `json:"branch"`
Access string `json:"access"`
PublicKey string `json:"public_key,omitempty"`
TokenSet bool `json:"token_set"`
AutoDeploy bool `json:"auto_deploy"`
AutoPaused bool `json:"auto_paused"`
Blocked bool `json:"blocked"`
EnvTracked bool `json:"env_tracked"`
Cloned bool `json:"cloned"`
Head *GitHeadView `json:"head"`
Deployed *GitDeployedView `json:"deployed"`
Check *GitCheckView `json:"check"`
NewCommits bool `json:"new_commits"`
State string `json:"state"`
Operation *gitOperation `json:"operation"`
History []GitHistoryView `json:"history"`
Compose *GitComposeView `json:"compose"`
// ComposeExample is set when the last check found no compose file in the repository.
ComposeExample *string `json:"compose_example"`
EnvTemplate *string `json:"env_template"`
BuildLog string `json:"build_log"`
Webhook GitWebhookView `json:"webhook"`
// Follow is `branch` or `tags`. TagPattern and Prereleases choose the tags an app that
// follows them may deploy, and are kept while it follows a branch.
Follow string `json:"follow"`
TagPattern string `json:"tag_pattern"`
Prereleases bool `json:"prereleases"`
}
GitAppView is the `GitApp` of the API.
func CheckGitApp ¶ added in v0.4.57
func CheckGitApp(ctx context.Context, name string) (*GitAppView, error)
CheckGitApp is "Check now". An adoptable app is adopted here, so that a folder that cannot be adopted is refused with its reason; the check itself runs in the background, then whatever the automatic rebuild may deploy, and the app is returned as the check starts.
func CreateGitApp ¶ added in v0.4.57
func CreateGitApp(ctx context.Context, registration GitAppRegistration) (*GitAppView, error)
CreateGitApp registers an app to deploy from a repository. Nothing is cloned: a check does that, once the owner has given the repository the deploy key.
func DeployGitApp ¶ added in v0.4.57
DeployGitApp deploys commit, or the remote's latest when commit is empty (for an app that follows tags, the highest eligible tag), in the background. A commit of the history is a revert. env is written as .env first.
func DeployGitAppTag ¶ added in v0.4.62
DeployGitAppTag deploys tag, one of the eligible tags of an app that follows tags, lower ones included, or the highest when tag is empty, in the background. A tag whose commit ran is a revert. env is written as .env first.
func GetGitApp ¶ added in v0.4.57
func GetGitApp(ctx context.Context, name string) (*GitAppView, error)
GetGitApp is the app as the API answers it.
func UpdateGitApp ¶ added in v0.4.57
func UpdateGitApp(ctx context.Context, name string, changes GitAppChanges) (*GitAppView, error)
UpdateGitApp changes the branch, the automatic rebuild, the access, the webhook or the mode of a git app, and adopts an adoptable one.
type GitCheckView ¶ added in v0.4.62
type GitCheckView struct {
At time.Time `json:"at"`
RemoteCommit string `json:"remote_commit"`
RemoteTag string `json:"remote_tag"`
TagMoved bool `json:"tag_moved"`
Error string `json:"error"`
}
GitCheckView is the `check` of the API: the last check, and whether it found the deployed tag on another commit.
type GitComposeServiceView ¶ added in v0.4.57
type GitComposeView ¶ added in v0.4.57
type GitComposeView struct {
Files []string `json:"files"`
Services []GitComposeServiceView `json:"services"`
}
type GitDeployedView ¶ added in v0.4.57
type GitHeadView ¶ added in v0.4.57
type GitHistoryView ¶ added in v0.4.57
type GitRequestError ¶ added in v0.4.57
type GitRequestError string
GitRequestError is a request that cannot be carried out as it is; the API answers 400 with its message, which says what to change.
func (GitRequestError) Error ¶ added in v0.4.57
func (e GitRequestError) Error() string
type GitWebhookDelivery ¶ added in v0.4.61
type GitWebhookDelivery struct {
At time.Time `json:"at"`
Forge string `json:"forge"`
Event string `json:"event"`
Result string `json:"result"`
}
GitWebhookDelivery is the last delivery whose signature was valid.
type GitWebhookView ¶ added in v0.4.61
type GitWebhookView struct {
Enabled bool `json:"enabled"`
Path string `json:"path"`
// Secret is set while the webhook is on.
Secret string `json:"secret,omitempty"`
LastDelivery *GitWebhookDelivery `json:"last_delivery"`
}
GitWebhookView is the `webhook` of a git app in the API.
type LogsSettings ¶ added in v0.4.85
type LogsSettings struct {
RetentionDays int
MaxSizeMB int
IncludeOthers bool
UsedMB float64
OldestAt *time.Time
Path string
}
func GetLogsSettings ¶ added in v0.4.85
func GetLogsSettings() LogsSettings
type ProgressDetail ¶
type PullOut ¶
type PullOut struct {
Status StatusType `json:"status"`
ProgressDetail ProgressDetail `json:"progressDetail"`
Id string `json:"id"`
}
type ResticCheck ¶ added in v0.4.82
type ResticCheck struct {
At time.Time `json:"at"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
}
ResticCheck is the last check of one destination.
type RestoreInstaller ¶ added in v0.4.41
type RestoreInstaller func(ctx context.Context, name string, compose, env []byte) (*ComposeApp, error)
RestoreInstaller puts an app that is not installed back from its compose file and, when the backup holds one, its .env, and returns it once its containers exist -- so that its named volumes exist too and have somewhere to be restored.
type RestoreOptions ¶ added in v0.4.41
type RestoreOptions struct {
Destination string
App string
Stamp string
// Paths, when there are any, restore only those paths of the backup -- as browsing
// it names them -- onto the app as it is, over what is there and deleting nothing.
// Empty restores the whole backup, as it was.
Paths []string
// Containers lists the app's containers; nil means ask Docker. A seam for the
// tests, as on BackupOptions.
Containers func(ctx context.Context) (map[string][]codegen.ContainerSummary, error)
}
RestoreOptions is one restore.
type RestoreReport ¶ added in v0.4.41
type RestoreReport struct {
// Restored, in the order it happened, with this host's paths
Restored []BackupOperation
// Missing is in the backup and has nowhere to go on this app: a volume it no
// longer declares, a bind mount it no longer has. Reported, never guessed.
Missing []BackupOperation
// Skipped is what the backup itself left out, copied from its manifest so the
// person restoring finds out here rather than by noticing something absent.
Skipped []BackupEntry
// Installed is true when the app was not installed and has been, from the
// backup's own compose file.
Installed bool
}
RestoreReport is what a restore did, and what it could not.
func RestoreBackup ¶ added in v0.4.41
func RestoreBackup(ctx context.Context, installed *ComposeApp, docker backupDocker, restorer BackupRestorer, install RestoreInstaller, opts RestoreOptions) (RestoreReport, error)
RestoreBackup puts opts.App back the way the backup at opts.Stamp had it.
installed is the app as it is now, or nil when it is not installed at all -- in which case it is installed first, from the compose file the backup holds. The app is held still for the length of the copy either way, and started again afterwards, whatever happened in between.
func RestoreOnDemand ¶ added in v0.4.41
func RestoreOnDemand(ctx context.Context, installed *ComposeApp, docker backupDocker, restorer BackupRestorer, install RestoreInstaller, opts RestoreOptions) (RestoreReport, error)
RestoreOnDemand runs one restore somebody asked for, and records it in the same log as the backups, marked as a restore.
func RestoreSystemBackup ¶ added in v0.4.45
func RestoreSystemBackup(ctx context.Context, restorer BackupRestorer, units unitControl, opts SystemRestoreOptions) (RestoreReport, error)
RestoreSystemBackup puts the box's own state back the way the backup had it.
Where things go is this box's list, never the manifest's paths, as with an app. The services holding the files are stopped for the copy and started again whatever happens; rclone is restarted last, after everything has been copied through it, if its configuration was among what came back.
func SystemRestoreOnDemand ¶ added in v0.4.45
func SystemRestoreOnDemand(ctx context.Context, restorer BackupRestorer, units unitControl, opts SystemRestoreOptions) (RestoreReport, error)
SystemRestoreOnDemand runs one restore of the box and records it.
type ScheduleEvery ¶ added in v0.4.37
type ScheduleEvery string
ScheduleEvery is how often a schedule comes round.
const ( ScheduleDaily ScheduleEvery = "daily" ScheduleWeekly ScheduleEvery = "weekly" )
type Services ¶
type Services interface {
AppStoreManagement() *AppStoreManagement
// Git() *GitService
Compose() *ComposeService
Docker() DockerService
Gateway() external.ManagementService
Notify() external.NotifyService
MessageBus() *message_bus.ClientWithResponses
}
var (
MyService Services
)
func NewService ¶
type StatusType ¶
type StatusType string
const ( Pull StatusType = "Pulling fs layer" PullComplete StatusType = "Pull complete" // AlreadyExists is what the daemon sends for a layer this host already has. It // arrives INSTEAD of the pulling/complete pair, never alongside it, so a layer // reported this way is one that is both known and finished. AlreadyExists StatusType = "Already exists" )
type SystemBackupOptions ¶ added in v0.4.45
type SystemBackupOptions struct {
Destination string
Stamp string
// HoldStill stops the services that hold the files open for the copy.
HoldStill bool
}
SystemBackupOptions is one backup of the box.
type SystemRestoreOptions ¶ added in v0.4.45
SystemRestoreOptions is one restore of the box.
Source Files
¶
- app.go
- app_operations.go
- appstore.go
- appstore_management.go
- appstore_mirror.go
- backup_browse.go
- backup_contents.go
- backup_dns.go
- backup_events.go
- backup_hold.go
- backup_hold_recovery.go
- backup_inventory.go
- backup_log.go
- backup_plan.go
- backup_record.go
- backup_restore.go
- backup_retention.go
- backup_run.go
- backup_schedule.go
- backup_scheduled_runner.go
- backup_scheduler.go
- backup_system.go
- backup_upgrade_hold.go
- compose_app.go
- compose_env.go
- compose_fallback.go
- compose_home.go
- compose_install_env.go
- compose_numeric_strings.go
- compose_recovery.go
- compose_service.go
- compose_update_all.go
- container.go
- container_removal.go
- container_stats.go
- datapath.go
- docker_watch.go
- errs.go
- git_app_state.go
- git_app_view.go
- git_apps.go
- git_deploy.go
- git_lifecycle.go
- git_restore.go
- git_runtime.go
- git_tags.go
- git_webhook.go
- git_webhook_receive.go
- git_webhook_signature.go
- image.go
- image_prune.go
- image_update.go
- logs.go
- logs_follow.go
- restic_check.go
- service.go
- stats.go
- utils.go