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