1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/errors"
12
"github.com/juju/utils/featureflag"
15
// record represents an entry in a Registry.
18
facadeType reflect.Type
19
// If the feature is not the empty string, then this facade
20
// is only returned when that feature flag is set.
22
// It is not a good thing that we depend on yet another flavour
23
// of global in the implementation of the Registry that itself
24
// only meaningfully exists as a global.
28
// versions is our internal structure for tracking specific versions of a
29
// single facade. We use a map to be able to quickly lookup a version.
30
type versions map[int]record
32
// Registry describes Facades the facades exposed by some API server.
34
// It's only actually used as a global -- `apiserver/common.Facades` --
35
// but if we were smarter we could just create a Registry directly and
36
// pass it into the apiserver.
37
type Registry struct {
38
facades map[string]versions
41
// Register adds a single named facade at a given version to the registry.
42
// Factory will be called when someone wants to instantiate an object of
43
// this facade, and facadeType defines the concrete type that the returned object will be.
44
// The Type information is used to define what methods will be exported in the
45
// API, and it must exactly match the actual object returned by the factory.
46
func (f *Registry) Register(name string, version int, factory Factory, facadeType reflect.Type, feature string) error {
48
f.facades = make(map[string]versions, 1)
52
facadeType: facadeType,
55
if vers, ok := f.facades[name]; ok {
56
if _, ok := vers[version]; ok {
57
fullname := fmt.Sprintf("%s(%d)", name, version)
58
return fmt.Errorf("object %q already registered", fullname)
60
vers[version] = record
62
f.facades[name] = versions{version: record}
67
// lookup translates a facade name and version into a record.
68
func (f *Registry) lookup(name string, version int) (record, error) {
69
if versions, ok := f.facades[name]; ok {
70
if record, ok := versions[version]; ok {
71
if featureflag.Enabled(record.feature) {
76
return record{}, errors.NotFoundf("%s(%d)", name, version)
79
// GetFactory returns just the Factory for a given Facade name and version.
80
// See also GetType for getting the type information instead of the creation factory.
81
func (f *Registry) GetFactory(name string, version int) (Factory, error) {
82
record, err := f.lookup(name, version)
86
return record.factory, nil
89
// GetType returns the type information for a given Facade name and version.
90
// This can be used for introspection purposes (to determine what methods are
92
func (f *Registry) GetType(name string, version int) (reflect.Type, error) {
93
record, err := f.lookup(name, version)
97
return record.facadeType, nil
100
// Description describes the name and what versions of a facade have been
102
type Description struct {
107
// descriptionFromVersions aggregates the information in a versions map into a
108
// more friendly form for List().
109
func descriptionFromVersions(name string, vers versions) Description {
110
intVersions := make([]int, 0, len(vers))
111
for version, record := range vers {
112
if featureflag.Enabled(record.feature) {
113
intVersions = append(intVersions, version)
116
sort.Ints(intVersions)
119
Versions: intVersions,
123
// List returns a slice describing each of the registered Facades.
124
func (f *Registry) List() []Description {
125
names := make([]string, 0, len(f.facades))
126
for name := range f.facades {
127
names = append(names, name)
130
descriptions := make([]Description, 0, len(f.facades))
131
for _, name := range names {
132
facades := f.facades[name]
133
description := descriptionFromVersions(name, facades)
134
if len(description.Versions) > 0 {
135
descriptions = append(descriptions, description)
141
// Discard gets rid of a registration that has already been done. Calling
142
// discard on an entry that is not present is not considered an error.
143
func (f *Registry) Discard(name string, version int) {
144
if versions, ok := f.facades[name]; ok {
145
delete(versions, version)
146
if len(versions) == 0 {
147
delete(f.facades, name)