Add the builtin org.mobyproject.jobs.v0 extension, serving the Jobs API on the daemon socket. It is experimental and disabled by default; enable it with extensions.enable (--extension-enable). Run containers are driven through an Engine API client from the daemon API point, and their exits observed through the container state point, so restart-policy attempts do not count as exits. Jobs persist through the storage point, and unnamed jobs are named through the name-generator point. Removing a run container also removes its anonymous volumes. The extension host initializes before the daemon serves its API, so activation reconciles runs and starts the scheduler in the background, and the Jobs API answers Unavailable until then. Shutdown releases blocked calls such as Wait, and waits for activation and in-flight calls only until its deadline. Authorization plugins are not supported yet, as the README states. Integration tests cover a manual run, reconciliation after a killed daemon, and the disabled extension. A README next to the contract describes how to use it. Co-authored-by: Paweł Gronowski <pawel.gronowski@docker.com> Signed-off-by: Guillaume Lours <glours@users.noreply.github.com> Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
120 lines
4.2 KiB
Go
120 lines
4.2 KiB
Go
package daemon
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/moby/extensions"
|
|
"github.com/moby/extensions/clientpoint"
|
|
"github.com/moby/extensions/serverpoint"
|
|
"github.com/moby/moby/v2/daemon/config"
|
|
"github.com/moby/moby/v2/daemon/internal/extensionapi"
|
|
"github.com/moby/moby/v2/daemon/internal/extensionconfig"
|
|
"github.com/moby/moby/v2/daemon/internal/extensionstorage/bbolt"
|
|
"github.com/moby/moby/v2/daemon/internal/jobs"
|
|
|
|
containernamegeneratorpb "github.com/moby/moby/v2/extpoints/containernamegenerator/v0/protogen"
|
|
containerstatepb "github.com/moby/moby/v2/extpoints/containerstate/v0/protogen"
|
|
daemonapipb "github.com/moby/moby/v2/extpoints/daemonapi/v0/protogen"
|
|
daemonconfigpb "github.com/moby/moby/v2/extpoints/daemonconfig/v0/protogen"
|
|
jobspb "github.com/moby/moby/v2/extpoints/jobs/api/v0/protogen"
|
|
servicenamegeneratorpb "github.com/moby/moby/v2/extpoints/servicenamegenerator/v0/protogen"
|
|
storagepb "github.com/moby/moby/v2/extpoints/storage/kv/v0/protogen"
|
|
namesgeneratorlegacy "github.com/moby/moby/v2/internal/namesgenerator/legacy"
|
|
)
|
|
|
|
// builtinExtensions lists in-tree extensions that should be available in the
|
|
// engine by default.
|
|
var builtinExtensions = []extensions.Extension{
|
|
namesgeneratorlegacy.Extension,
|
|
bbolt.Extension,
|
|
}
|
|
|
|
// optionalExtensions lists built-ins that remain inactive until explicitly
|
|
// enabled through extensions.enable.
|
|
func (daemon *Daemon) optionalExtensions() []extensions.Extension {
|
|
return []extensions.Extension{
|
|
jobs.NewExtension(),
|
|
}
|
|
}
|
|
|
|
// daemonExtensions declares extensions that need daemon-owned state.
|
|
// Ordinary builtin definitions obtain their bootstrap settings through points.
|
|
func (daemon *Daemon) daemonExtensions() []extensions.Extension {
|
|
cfg := daemon.Config()
|
|
return []extensions.Extension{
|
|
extensionconfig.New(&cfg),
|
|
newMonolithExtension(daemon),
|
|
extensionapi.New(daemon.extensionAPIEndpoint),
|
|
}
|
|
}
|
|
|
|
// selectBuiltinExtensions applies activation settings to whole extensions.
|
|
// Provider conflicts and required dependencies are validated by the host.
|
|
func selectBuiltinExtensions(cfg config.ExtensionsConfig, defaults, optional []extensions.Extension) ([]extensions.Extension, error) {
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
all := slices.Concat(defaults, optional)
|
|
enabled := make(map[string]bool, len(all))
|
|
for _, ext := range optional {
|
|
enabled[string(ext.Declaration().ID)] = false
|
|
}
|
|
for _, ext := range defaults {
|
|
enabled[string(ext.Declaration().ID)] = true
|
|
}
|
|
for _, id := range slices.Concat(cfg.Enable, cfg.Disable) {
|
|
if _, known := enabled[id]; !known {
|
|
return nil, fmt.Errorf("extensions: unknown built-in extension %q (available: %s)", id, strings.Join(slices.Sorted(maps.Keys(enabled)), ", "))
|
|
}
|
|
}
|
|
for _, id := range cfg.Enable {
|
|
enabled[id] = true
|
|
}
|
|
for _, id := range cfg.Disable {
|
|
enabled[id] = false
|
|
}
|
|
return slices.DeleteFunc(all, func(ext extensions.Extension) bool {
|
|
return !enabled[string(ext.Declaration().ID)]
|
|
}), nil
|
|
}
|
|
|
|
// clientProviders lists generated client wiring for points that launched
|
|
// extensions may provide.
|
|
//
|
|
// A point that's simply exposed as a service by an extension DOES NOT need to
|
|
// be in this list.
|
|
// This list only contains points that the daemon calls directly.
|
|
func clientProviders() []clientpoint.Registration {
|
|
return []clientpoint.Registration{
|
|
containernamegeneratorpb.ClientPoint,
|
|
servicenamegeneratorpb.ClientPoint,
|
|
}
|
|
}
|
|
|
|
// offeredPointServers lists generated server wiring for points that built-in
|
|
// extensions offer through servicev0.Offer.
|
|
// Missing entries fail daemon startup if the offering extension is enabled.
|
|
// Out-of-process extensions register their own servers and need no entries here.
|
|
// Points that are not offered and service.grpc services also need no entries.
|
|
func offeredPointServers() []serverpoint.Registration {
|
|
return []serverpoint.Registration{
|
|
jobspb.ServerPoint,
|
|
}
|
|
}
|
|
|
|
// dependencyProviders lists points that external extensions may call back
|
|
// into.
|
|
// Points not in this list will not be able to used as dependencies.
|
|
func dependencyProviders() []serverpoint.Registration {
|
|
return []serverpoint.Registration{
|
|
storagepb.ServerPoint,
|
|
daemonconfigpb.ServerPoint,
|
|
containerstatepb.ServerPoint,
|
|
daemonapipb.ServerPoint,
|
|
}
|
|
}
|