forked from juju/juju
-
Notifications
You must be signed in to change notification settings - Fork 0
/
origin.go
231 lines (198 loc) · 6.43 KB
/
origin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package logfwd
import (
"fmt"
"github.com/juju/errors"
"github.com/juju/names/v4"
"github.com/juju/version/v2"
)
// canonicalPEN is the IANA-registered Private Enterprise Number
// assigned to Canonical. Among other things, this is used in RFC 5424
// structured data.
//
// See https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
const canonicalPEN = 28978
// These are the recognized origin types.
const (
OriginTypeUnknown OriginType = 0
OriginTypeUser = iota
OriginTypeMachine
OriginTypeUnit
)
var originTypes = map[OriginType]string{
OriginTypeUnknown: "unknown",
OriginTypeUser: names.UserTagKind,
OriginTypeMachine: names.MachineTagKind,
OriginTypeUnit: names.UnitTagKind,
}
// OriginType is the "enum" type for the different kinds of log record
// origin.
type OriginType int
// ParseOriginType converts a string to an OriginType or fails if
// not able. It round-trips with String().
func ParseOriginType(value string) (OriginType, error) {
for ot, str := range originTypes {
if value == str {
return ot, nil
}
}
const originTypeInvalid OriginType = -1
return originTypeInvalid, errors.Errorf("unrecognized origin type %q", value)
}
// String returns a string representation of the origin type.
func (ot OriginType) String() string {
return originTypes[ot]
}
// Validate ensures that the origin type is correct.
func (ot OriginType) Validate() error {
// As noted above, typedef'ing int means that the use of int
// literals or explicit type conversion could result in unsupported
// "enum" values. Otherwise OriginType would not need this method.
if _, ok := originTypes[ot]; !ok {
return errors.NewNotValid(nil, "unsupported origin type")
}
return nil
}
// ValidateName ensures that the given origin name is valid within the
// context of the origin type.
func (ot OriginType) ValidateName(name string) error {
switch ot {
case OriginTypeUnknown:
if name != "" {
return errors.NewNotValid(nil, "origin name must not be set if type is unknown")
}
case OriginTypeUser:
if !names.IsValidUser(name) {
return errors.NewNotValid(nil, "bad user name")
}
case OriginTypeMachine:
if !names.IsValidMachine(name) {
return errors.NewNotValid(nil, "bad machine name")
}
case OriginTypeUnit:
if !names.IsValidUnit(name) {
return errors.NewNotValid(nil, "bad unit name")
}
}
return nil
}
// Origin describes what created the record.
type Origin struct {
// ControllerUUID is the ID of the Juju controller under which the
// record originated.
ControllerUUID string
// ModelUUID is the ID of the Juju model under which the record
// originated.
ModelUUID string
// Hostname identifies the host where the record originated.
Hostname string
// Type identifies the kind of thing that generated the record.
Type OriginType
// Name identifies the thing that generated the record.
Name string
// Software identifies the running software that created the record.
Software Software
}
// OriginForMachineAgent populates a new origin for the agent.
func OriginForMachineAgent(tag names.MachineTag, controller, model string, ver version.Number) Origin {
return originForAgent(OriginTypeMachine, tag, controller, model, ver)
}
// OriginForUnitAgent populates a new origin for the agent.
func OriginForUnitAgent(tag names.UnitTag, controller, model string, ver version.Number) Origin {
return originForAgent(OriginTypeUnit, tag, controller, model, ver)
}
func originForAgent(oType OriginType, tag names.Tag, controller, model string, ver version.Number) Origin {
origin := originForJuju(oType, tag.Id(), controller, model, ver)
origin.Hostname = fmt.Sprintf("%s.%s", tag, model)
origin.Software.Name = fmt.Sprintf("jujud-%s-agent", tag.Kind())
return origin
}
// OriginForJuju populates a new origin for the juju client.
func OriginForJuju(tag names.Tag, controller, model string, ver version.Number) (Origin, error) {
oType, err := ParseOriginType(tag.Kind())
if err != nil {
return Origin{}, errors.Annotate(err, "invalid tag")
}
return originForJuju(oType, tag.Id(), controller, model, ver), nil
}
func originForJuju(oType OriginType, name, controller, model string, ver version.Number) Origin {
return Origin{
ControllerUUID: controller,
ModelUUID: model,
Type: oType,
Name: name,
Software: Software{
PrivateEnterpriseNumber: canonicalPEN,
Name: "juju",
Version: ver,
},
}
}
// Validate ensures that the origin is correct.
func (o Origin) Validate() error {
if o.ControllerUUID == "" {
return errors.NewNotValid(nil, "empty ControllerUUID")
}
if !names.IsValidModel(o.ControllerUUID) {
return errors.NewNotValid(nil, fmt.Sprintf("ControllerUUID %q not a valid UUID", o.ControllerUUID))
}
if o.ModelUUID == "" {
return errors.NewNotValid(nil, "empty ModelUUID")
}
if !names.IsValidModel(o.ModelUUID) {
return errors.NewNotValid(nil, fmt.Sprintf("ModelUUID %q not a valid UUID", o.ModelUUID))
}
if err := o.Type.Validate(); err != nil {
return errors.Annotate(err, "invalid Type")
}
if o.Name == "" && o.Type != OriginTypeUnknown {
return errors.NewNotValid(nil, "empty Name")
}
if err := o.Type.ValidateName(o.Name); err != nil {
return errors.Annotatef(err, "invalid Name %q", o.Name)
}
if !o.Software.isZero() {
if err := o.Software.Validate(); err != nil {
return errors.Annotate(err, "invalid Software")
}
}
return nil
}
// Software describes a running application.
type Software struct {
// PrivateEnterpriseNumber is the IANA-registered "SMI Network
// Management Private Enterprise Code" for the software's vendor.
//
// See https://tools.ietf.org/html/rfc5424#section-7.2.2.
PrivateEnterpriseNumber int
// Name identifies the software (relative to the vendor).
Name string
// Version is the software's version.
Version version.Number
}
func (sw Software) isZero() bool {
if sw.PrivateEnterpriseNumber > 0 {
return false
}
if sw.Name != "" {
return false
}
if sw.Version != version.Zero {
return false
}
return true
}
// Validate ensures that the software info is correct.
func (sw Software) Validate() error {
if sw.PrivateEnterpriseNumber <= 0 {
return errors.NewNotValid(nil, "missing PrivateEnterpriseNumber")
}
if sw.Name == "" {
return errors.NewNotValid(nil, "empty Name")
}
if sw.Version == version.Zero {
return errors.NewNotValid(nil, "empty Version")
}
return nil
}