~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/facade/registry.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package facade
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "reflect"
 
9
        "sort"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        "github.com/juju/utils/featureflag"
 
13
)
 
14
 
 
15
// record represents an entry in a Registry.
 
16
type record struct {
 
17
        factory    Factory
 
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.
 
21
        //
 
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.
 
25
        feature string
 
26
}
 
27
 
 
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
 
31
 
 
32
// Registry describes Facades the facades exposed by some API server.
 
33
//
 
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
 
39
}
 
40
 
 
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 {
 
47
        if f.facades == nil {
 
48
                f.facades = make(map[string]versions, 1)
 
49
        }
 
50
        record := record{
 
51
                factory:    factory,
 
52
                facadeType: facadeType,
 
53
                feature:    feature,
 
54
        }
 
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)
 
59
                }
 
60
                vers[version] = record
 
61
        } else {
 
62
                f.facades[name] = versions{version: record}
 
63
        }
 
64
        return nil
 
65
}
 
66
 
 
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) {
 
72
                                return record, nil
 
73
                        }
 
74
                }
 
75
        }
 
76
        return record{}, errors.NotFoundf("%s(%d)", name, version)
 
77
}
 
78
 
 
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)
 
83
        if err != nil {
 
84
                return nil, err
 
85
        }
 
86
        return record.factory, nil
 
87
}
 
88
 
 
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
 
91
// available, etc).
 
92
func (f *Registry) GetType(name string, version int) (reflect.Type, error) {
 
93
        record, err := f.lookup(name, version)
 
94
        if err != nil {
 
95
                return nil, err
 
96
        }
 
97
        return record.facadeType, nil
 
98
}
 
99
 
 
100
// Description describes the name and what versions of a facade have been
 
101
// registered.
 
102
type Description struct {
 
103
        Name     string
 
104
        Versions []int
 
105
}
 
106
 
 
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)
 
114
                }
 
115
        }
 
116
        sort.Ints(intVersions)
 
117
        return Description{
 
118
                Name:     name,
 
119
                Versions: intVersions,
 
120
        }
 
121
}
 
122
 
 
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)
 
128
        }
 
129
        sort.Strings(names)
 
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)
 
136
                }
 
137
        }
 
138
        return descriptions
 
139
}
 
140
 
 
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)
 
148
                }
 
149
        }
 
150
}