github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/message/event.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package message 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 22 "github.com/datastax/go-cassandra-native-protocol/primitive" 23 ) 24 25 type Event interface { 26 Message 27 GetEventType() primitive.EventType 28 } 29 30 // SCHEMA CHANGE EVENT 31 32 // SchemaChangeEvent is a response sent when a schema change event occurs. 33 // Note: this struct is identical to SchemaChangeResult. 34 // +k8s:deepcopy-gen=true 35 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 36 type SchemaChangeEvent struct { 37 // The schema change type. 38 ChangeType primitive.SchemaChangeType 39 // The schema change target, that is, the kind of schema object affected by the change. 40 Target primitive.SchemaChangeTarget 41 // The name of the keyspace affected by the change. 42 Keyspace string 43 // If the schema object affected by the change is not the keyspace itself, this field contains its name. Otherwise, 44 // this field is irrelevant and should be empty. 45 Object string 46 // If the schema object affected by the change is a function or an aggregate, this field contains its arguments. 47 // Otherwise, this field is irrelevant. Valid from protocol version 4 onwards. 48 Arguments []string 49 } 50 51 func (m *SchemaChangeEvent) IsResponse() bool { 52 return true 53 } 54 55 func (m *SchemaChangeEvent) GetOpCode() primitive.OpCode { 56 return primitive.OpCodeEvent 57 } 58 59 func (m *SchemaChangeEvent) GetEventType() primitive.EventType { 60 return primitive.EventTypeSchemaChange 61 } 62 63 func (m *SchemaChangeEvent) String() string { 64 return fmt.Sprintf("EVENT SCHEMA CHANGE (type=%v target=%v keyspace=%v object=%v args=%v)", 65 m.ChangeType, 66 m.Target, 67 m.Keyspace, 68 m.Object, 69 m.Arguments) 70 } 71 72 // STATUS CHANGE EVENT 73 74 // StatusChangeEvent is a response sent when a node status change event occurs. 75 // +k8s:deepcopy-gen=true 76 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 77 type StatusChangeEvent struct { 78 ChangeType primitive.StatusChangeType 79 Address *primitive.Inet 80 } 81 82 func (m *StatusChangeEvent) IsResponse() bool { 83 return true 84 } 85 86 func (m *StatusChangeEvent) GetOpCode() primitive.OpCode { 87 return primitive.OpCodeEvent 88 } 89 90 func (m *StatusChangeEvent) GetEventType() primitive.EventType { 91 return primitive.EventTypeStatusChange 92 } 93 94 func (m *StatusChangeEvent) String() string { 95 return fmt.Sprintf("EVENT STATUS CHANGE (type=%v address=%v)", m.ChangeType, m.Address) 96 } 97 98 // TOPOLOGY CHANGE EVENT 99 100 // TopologyChangeEvent is a response sent when a topology change event occurs. 101 // +k8s:deepcopy-gen=true 102 // +k8s:deepcopy-gen:interfaces=github.com/datastax/go-cassandra-native-protocol/message.Message 103 type TopologyChangeEvent struct { 104 // The topology change type. Note that MOVED_NODE is only valid from protocol version 3 onwards. 105 ChangeType primitive.TopologyChangeType 106 // The address of the node. 107 Address *primitive.Inet 108 } 109 110 func (m *TopologyChangeEvent) IsResponse() bool { 111 return true 112 } 113 114 func (m *TopologyChangeEvent) GetOpCode() primitive.OpCode { 115 return primitive.OpCodeEvent 116 } 117 118 func (m *TopologyChangeEvent) GetEventType() primitive.EventType { 119 return primitive.EventTypeTopologyChange 120 } 121 122 func (m *TopologyChangeEvent) String() string { 123 return fmt.Sprintf("EVENT TOPOLOGY CHANGE (type=%v address=%v)", m.ChangeType, m.Address) 124 } 125 126 // EVENT CODEC 127 128 type eventCodec struct{} 129 130 func (c *eventCodec) Encode(msg Message, dest io.Writer, version primitive.ProtocolVersion) (err error) { 131 event, ok := msg.(Event) 132 if !ok { 133 return fmt.Errorf("expected message.Event, got %T", msg) 134 } 135 if err = primitive.CheckValidEventType(event.GetEventType()); err != nil { 136 return err 137 } else if err = primitive.WriteString(string(event.GetEventType()), dest); err != nil { 138 return fmt.Errorf("cannot write EVENT type: %v", err) 139 } 140 switch event.GetEventType() { 141 case primitive.EventTypeSchemaChange: 142 sce, ok := msg.(*SchemaChangeEvent) 143 if !ok { 144 return fmt.Errorf("expected *message.SchemaChangeEvent, got %T", msg) 145 } 146 if err = primitive.CheckValidSchemaChangeType(sce.ChangeType); err != nil { 147 return err 148 } else if err = primitive.WriteString(string(sce.ChangeType), dest); err != nil { 149 return fmt.Errorf("cannot write SchemaChangeEvent.ChangeType: %w", err) 150 } 151 if version >= primitive.ProtocolVersion3 { 152 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 153 return err 154 } else if err = primitive.WriteString(string(sce.Target), dest); err != nil { 155 return fmt.Errorf("cannot write SchemaChangeEvent.Target: %w", err) 156 } 157 if sce.Keyspace == "" { 158 return errors.New("EVENT SchemaChange: cannot write empty keyspace") 159 } else if err = primitive.WriteString(sce.Keyspace, dest); err != nil { 160 return fmt.Errorf("cannot write SchemaChangeEvent.Keyspace: %w", err) 161 } 162 switch sce.Target { 163 case primitive.SchemaChangeTargetKeyspace: 164 case primitive.SchemaChangeTargetTable: 165 fallthrough 166 case primitive.SchemaChangeTargetType: 167 if sce.Object == "" { 168 return errors.New("EVENT SchemaChange: cannot write empty object") 169 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 170 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 171 } 172 case primitive.SchemaChangeTargetAggregate: 173 fallthrough 174 case primitive.SchemaChangeTargetFunction: 175 if sce.Keyspace == "" { 176 return errors.New("EVENT SchemaChange: cannot write empty object") 177 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 178 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 179 } 180 if err = primitive.WriteStringList(sce.Arguments, dest); err != nil { 181 return fmt.Errorf("cannot write SchemaChangeEvent.Arguments: %w", err) 182 } 183 } 184 } else { 185 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 186 return err 187 } 188 if sce.Keyspace == "" { 189 return errors.New("EVENT SchemaChange: cannot write empty keyspace") 190 } else if err = primitive.WriteString(sce.Keyspace, dest); err != nil { 191 return fmt.Errorf("cannot write SchemaChangeEvent.Keyspace: %w", err) 192 } 193 switch sce.Target { 194 case primitive.SchemaChangeTargetKeyspace: 195 if sce.Object != "" { 196 return errors.New("EVENT SchemaChange: table must be empty for keyspace targets") 197 } else if err = primitive.WriteString("", dest); err != nil { 198 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 199 } 200 case primitive.SchemaChangeTargetTable: 201 if sce.Object == "" { 202 return errors.New("EVENT SchemaChange: cannot write empty table") 203 } else if err = primitive.WriteString(sce.Object, dest); err != nil { 204 return fmt.Errorf("cannot write SchemaChangeEvent.Object: %w", err) 205 } 206 } 207 } 208 return nil 209 case primitive.EventTypeStatusChange: 210 sce, ok := msg.(*StatusChangeEvent) 211 if !ok { 212 return fmt.Errorf("expected *message.StatusChangeEvent, got %T", msg) 213 } 214 if err = primitive.CheckValidStatusChangeType(sce.ChangeType); err != nil { 215 return err 216 } else if err = primitive.WriteString(string(sce.ChangeType), dest); err != nil { 217 return fmt.Errorf("cannot write StatusChangeEvent.ChangeType: %w", err) 218 } 219 if err = primitive.WriteInet(sce.Address, dest); err != nil { 220 return fmt.Errorf("cannot write StatusChangeEvent.Address: %w", err) 221 } 222 return nil 223 case primitive.EventTypeTopologyChange: 224 tce, ok := msg.(*TopologyChangeEvent) 225 if !ok { 226 return fmt.Errorf("expected *message.TopologyChangeEvent, got %T", msg) 227 } 228 if err = primitive.CheckValidTopologyChangeType(tce.ChangeType, version); err != nil { 229 return err 230 } else if err = primitive.WriteString(string(tce.ChangeType), dest); err != nil { 231 return fmt.Errorf("cannot write TopologyChangeEvent.ChangeType: %w", err) 232 } 233 if err = primitive.WriteInet(tce.Address, dest); err != nil { 234 return fmt.Errorf("cannot write TopologyChangeEvent.Address: %w", err) 235 } 236 return nil 237 } 238 return fmt.Errorf("unknown EVENT type: %v", event.GetEventType()) 239 } 240 241 func (c *eventCodec) EncodedLength(msg Message, version primitive.ProtocolVersion) (length int, err error) { 242 event, ok := msg.(Event) 243 if !ok { 244 return -1, fmt.Errorf("expected message.Event, got %T", msg) 245 } 246 length = primitive.LengthOfString(string(event.GetEventType())) 247 switch event.GetEventType() { 248 case primitive.EventTypeSchemaChange: 249 sce, ok := msg.(*SchemaChangeEvent) 250 if !ok { 251 return -1, fmt.Errorf("expected *message.SchemaChangeEvent, got %T", msg) 252 } 253 length += primitive.LengthOfString(string(sce.ChangeType)) 254 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 255 return -1, err 256 } 257 if version >= primitive.ProtocolVersion3 { 258 length += primitive.LengthOfString(string(sce.Target)) 259 length += primitive.LengthOfString(sce.Keyspace) 260 switch sce.Target { 261 case primitive.SchemaChangeTargetKeyspace: 262 case primitive.SchemaChangeTargetTable: 263 fallthrough 264 case primitive.SchemaChangeTargetType: 265 length += primitive.LengthOfString(sce.Object) 266 case primitive.SchemaChangeTargetAggregate: 267 fallthrough 268 case primitive.SchemaChangeTargetFunction: 269 length += primitive.LengthOfString(sce.Object) 270 length += primitive.LengthOfStringList(sce.Arguments) 271 } 272 } else { 273 length += primitive.LengthOfString(sce.Keyspace) 274 length += primitive.LengthOfString(sce.Object) 275 } 276 return length, nil 277 case primitive.EventTypeStatusChange: 278 sce, ok := msg.(*StatusChangeEvent) 279 if !ok { 280 return -1, fmt.Errorf("expected *message.StatusChangeEvent, got %T", msg) 281 } 282 length += primitive.LengthOfString(string(sce.ChangeType)) 283 inetLength, err := primitive.LengthOfInet(sce.Address) 284 if err != nil { 285 return -1, fmt.Errorf("cannot compute length of StatusChangeEvent.Address: %w", err) 286 } 287 length += inetLength 288 return length, nil 289 case primitive.EventTypeTopologyChange: 290 tce, ok := msg.(*TopologyChangeEvent) 291 if !ok { 292 return -1, fmt.Errorf("expected *message.TopologyChangeEvent, got %T", msg) 293 } 294 length += primitive.LengthOfString(string(tce.ChangeType)) 295 inetLength, err := primitive.LengthOfInet(tce.Address) 296 if err != nil { 297 return -1, fmt.Errorf("cannot compute length of TopologyChangeEvent.Address: %w", err) 298 } 299 length += inetLength 300 return length, nil 301 } 302 return -1, fmt.Errorf("unknown EVENT type: %v", event.GetEventType()) 303 } 304 305 func (c *eventCodec) Decode(source io.Reader, version primitive.ProtocolVersion) (Message, error) { 306 eventType, err := primitive.ReadString(source) 307 if err != nil { 308 return nil, err 309 } 310 switch primitive.EventType(eventType) { 311 case primitive.EventTypeSchemaChange: 312 sce := &SchemaChangeEvent{} 313 var changeType string 314 if changeType, err = primitive.ReadString(source); err != nil { 315 return nil, fmt.Errorf("cannot read SchemaChangeEvent.ChangeType: %w", err) 316 } 317 sce.ChangeType = primitive.SchemaChangeType(changeType) 318 if version >= primitive.ProtocolVersion3 { 319 var target string 320 if target, err = primitive.ReadString(source); err != nil { 321 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Target: %w", err) 322 } 323 sce.Target = primitive.SchemaChangeTarget(target) 324 if err = primitive.CheckValidSchemaChangeTarget(sce.Target, version); err != nil { 325 return nil, err 326 } 327 if sce.Keyspace, err = primitive.ReadString(source); err != nil { 328 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Keyspace: %w", err) 329 } 330 switch sce.Target { 331 case primitive.SchemaChangeTargetKeyspace: 332 case primitive.SchemaChangeTargetTable: 333 fallthrough 334 case primitive.SchemaChangeTargetType: 335 if sce.Object, err = primitive.ReadString(source); err != nil { 336 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Object: %w", err) 337 } 338 case primitive.SchemaChangeTargetAggregate: 339 fallthrough 340 case primitive.SchemaChangeTargetFunction: 341 if sce.Object, err = primitive.ReadString(source); err != nil { 342 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Object: %w", err) 343 } 344 if sce.Arguments, err = primitive.ReadStringList(source); err != nil { 345 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Arguments: %w", err) 346 } 347 default: 348 return nil, fmt.Errorf("unknown schema change target: %v", sce.Target) 349 } 350 } else { 351 if sce.Keyspace, err = primitive.ReadString(source); err != nil { 352 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Keyspace: %w", err) 353 } 354 if sce.Object, err = primitive.ReadString(source); err != nil { 355 return nil, fmt.Errorf("cannot read SchemaChangeEvent.Object: %w", err) 356 } 357 if sce.Object == "" { 358 sce.Target = primitive.SchemaChangeTargetKeyspace 359 } else { 360 sce.Target = primitive.SchemaChangeTargetTable 361 } 362 } 363 return sce, nil 364 case primitive.EventTypeStatusChange: 365 sce := &StatusChangeEvent{} 366 var changeType string 367 if changeType, err = primitive.ReadString(source); err != nil { 368 return nil, fmt.Errorf("cannot read StatusChangeEvent.ChangeType: %w", err) 369 } 370 sce.ChangeType = primitive.StatusChangeType(changeType) 371 if sce.Address, err = primitive.ReadInet(source); err != nil { 372 return nil, fmt.Errorf("cannot read StatusChangeEvent.Address: %w", err) 373 } 374 return sce, nil 375 case primitive.EventTypeTopologyChange: 376 tce := &TopologyChangeEvent{} 377 var changeType string 378 if changeType, err = primitive.ReadString(source); err != nil { 379 return nil, fmt.Errorf("cannot read TopologyChangeEvent.ChangeType: %w", err) 380 } 381 tce.ChangeType = primitive.TopologyChangeType(changeType) 382 if tce.Address, err = primitive.ReadInet(source); err != nil { 383 return nil, fmt.Errorf("cannot read TopologyChangeEvent.Address: %w", err) 384 } 385 return tce, nil 386 } 387 return nil, errors.New("unknown EVENT type: " + eventType) 388 } 389 390 func (c *eventCodec) GetOpCode() primitive.OpCode { 391 return primitive.OpCodeEvent 392 }