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  }